Unix/Linux是多任务的操作系统,它通过多个进程分别处理不同事务来实现,如果多个进程要进行协同工作或者争用同一个资源,互相之间的通讯就很有必要了。
进程间通信(Inter-Process Communication
,又称IPC
)是指一组由操作系统支持的机制,用于进程间的交互(如协调或通信)。在UNIX/Linux下主要有以下几种方式:
(1)管道(有名管道(pipe
)和无名管道(fifo
))(2)信号(signal
)(3)信号量(semaphore
)(4)消息队列(message``queues
)(5)共享内存(shared``memory
)(6)套接字(socket
)
消息队列提供了一种将数据块从一个进程发送到另一个进程的方法。每个数据块都被认为有一个类型,它可以实现消息的随机查询,消息不必按照先入先出的顺序读取,用户可以根据消息的类型读取它们。
和有名(pipe
)/无名(fifo
)管道一样,当从消息队列中读取一条消息的时候,消息队列中相应的数据会被删除。此外,它允许一个或多个进程向其写入或读取消息。每个消息队列都有一个消息队列标识,在整个系统中是唯一的;而且消息队列只有在内核重启或是手动删除(ipcrm
)消息队列时才会被删除。如果不手动删除消息队列,则消息队列将会一直存在于系统内核中。
消息队列和管道有着同样的缺点,即每个数据块的最大长度是上限,系统上所有队列的最大长度也是上限。每条消息的最大长度为最大值(MSGMAX
),每个消息队列的总字节数为最大值(MSGMNB
),系统上的消息队列总数也是最大值(MSGMNI
)。
宏MSGMNI
、MSGMAX
和MSGMNB
声明于/inclue/uapi/linux/msg.h
头文件中。其默认值如下所示:
#define MSGMNI 32000 /* <= IPCMNI */ /* max # of msg queue identifiers */
#define MSGMAX 8192 /* <= INT_MAX */ /* max size of message (bytes) */
#define MSGMNB 16384 /* <= INT_MAX */ /* default max size of a message queue */
在/proc/sys/kernel/
目录下,有相应的msgmni
、msgmax
和msgmnb
这3
个文件,里面记录了当前内核中与消息队列相关的几个参数值。
当然,这些默认值可通过命令sysctl
或系统函数sysctl()
进行修改。也可直接在/etc/sysctl.conf
文件中进行内核配置。
消息队列的本质其实是内核(kernel
)提供的一个链表,它实现了一个基于链表的数据结构。消息队列是基于消息的,而管道是基于字节流的,在某个进程往一个队列写入消息之前,并不需要另外某个进程在该消息队列上等待消息的到达。
对于有名管道(pipe
)和无名管道(fifo
),最后一次关闭发生时,仍在该管道(pipe
或fifo
)上的数据将被丢弃。有名管道(pipe
)或无名管道(fifo
)都是随进程持续的,而消息队列、信号量、共享内存都是随内核持续的。
在第一次创建消息队列时,内核会为其分配关联的队列控制块(Queue``Control``Block
, QCB
)、消息队列名称、唯一ID、内核缓冲区,队列长度、最大消息长度以及一个或多个等待列表。内核还采用开发人员提供的参数(例如队列长度和最大消息长度)来确定消息队列需要多少内存。内核在获得信息后,会从系统内存或某些私有内存空间为消息队列分配空间。
消息队列本身由许多元素组成,每个元素可以保存一条消息。其中保存第一条和最后一条消息的元素称为:头和尾。消息队列的内部原理大致如下图示所示:
消息队列具有两个相关联的任务的等待列表。接收任务等待列表由在队列为空时等待队列的任务组成。发送队列由队列满时等待队列的任务完成。
系统中记录消息队列的数据结构是struct``ipc_ids``msg_ids
,它位于内核中,系统中所有的消息队列都可以在结构msg_ids
中找到入口。
从Linux内核5.4.3
版本的msg.c
源文件可以得知,系统上每个现有的消息队列都有一个struct``msg_queue
数据结构。该数据类型声明如下:
struct msg_queue {
struct kern_ipc_perm q_perm; /* 消息队列操作访问权限 */
time64_t q_stime; /* 上一次调用msgsnd发送消息的时间 */
time64_t q_rtime; /* 上一次调用msgrcv接收消息的时间 */
time64_t q_ctime; /* 上一次修改的时间 */
unsigned long q_cbytes; /* 队列上当前字节数据 */
unsigned long q_qnum; /* 队列中的消息数目 */
unsigned long q_qbytes; /* 队列上最大字节数目 */
struct pid *q_lspid; /* 上一次调用msgsnd的pid */
struct pid *q_lrpid; /* 上一次接收消息的pid */
struct list_head q_messages;
struct list_head q_receivers;
struct list_head q_senders;
//这3个标注的内核链表用于管理睡眠的发送至(q_senders)、睡眠的接受者(q_receivers)和消息本身(q_messages)
} __randomize_layout;
该结构包含消息队列的状态信息以及队列的访问权限。成员q_stime
、q_rtime
、q_ctime
、q_cbytes
、q_qnum
、q_qbytes
、q_lspid
、q_lrpid
在上面的数据类型声明中采用注释的形式给出说明。
成员q_perm
用以说明该消息队列操作访问权限。对于struct``ipc_perm
数据类型,其声明如下:
struct ipc_perm
{
__kernel_key_t key; //消息队列全局唯一ID
__kernel_uid_t uid; //所有者有效UID
__kernel_gid_t gid; //所有者有效GID
__kernel_uid_t cuid; //创建者有效UID
__kernel_gid_t cgid; //创建者有效GID
__kernel_mode_t mode; //权限
unsigned short seq; //序列号
};
其中最值得关注的是成员key
,其类型为key_t
。这是系统对IPC资源的唯一标识符,这个标识符必须在申请IPC资源之前获得,我们可以通过系统函ftok()
获得。
而成员q_messages
、q_receivers
和q_senders
分别对应于消息本身、睡眠的接收者和睡眠的发送者。而q_messages
中的各个消息都封装在一个struct``msg_msg
的数据类型中,该结构声明如下:
// 双向链表指针
struct list_head {
struct list_head *next;
struct list_head *prev;
};
struct msg_msg {
struct list_head m_list;
long m_type; //消息类型
int m_ts; //消息正文长度
struct msg_msgseg *next;
/* 接下来是实际的消息*/
}
成员m_list
用于连接各个消息的链表元素,其他成员用于管理消息本身。其中m_type
指明消息类型,m_ts
用于指定消息正文长度(字节)。从该结构声明可以看到,struct``msg_msg
数据结构中没有声明成员用于存储消息本身。这是因为每个消息都至少分配了一个内存页,msg_msg
实例则保存在该页的起始处,剩余的空间可用于存储消息正文。这正是成员next
的作用,如果消息超过一个内存的长消息,则需要用next
(参考《深入Linux内核架构》)。
此外,睡眠消息接收者(q_receivers
)的数据结构声明分别如下:
/* one msg_receiver structure for each sleeping receiver */
struct msg_receiver {
struct list_head r_list;
struct task_struct *r_tsk;
int r_mode;
long r_msgtype;
long r_maxsize;
struct msg_msg *r_msg;
};
该结构声明保存了执行消息队列[进程] 的task_struct
的指针,以及对预期消息的描述(包括消息类型,消息最大长度等)、指向msg_msg
实例的一个指针。
而睡眠的消息生产者(q_senders
)的数据类型声明如下:
/* one msg_sender for each sleeping sender */
struct msg_sender {
struct list_head list;
struct task_struct *tsk;
size_t msgsz;
};
list
是链表,tsk
是指向对应进程的task_struct
指针。
根据前面的描述与介绍可以得出,Linux内核与消息独立建立起来的联系如下图所示:
与消息队列相关联的系统调用函数共有5
个,它们分别是:ftok()
、msgget()
、msgsnd()
、msgrcv()
和msgctl()
。下面将分别对这几个系统函数作详细的介绍。
系统函数ftok()
的函数原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
该函数将一个现有的路径名(pathname
)和一个整数标识符(带有proj_id
的低8
位)转换为key_t
值,称为IPC``key
,也就是所谓的IPC
唯一秘钥。
注意:它由两个参数(pathname
、proj_id
)生成key
。基本上,pathname
必须是这个进程可以读取的文件。另一个参数,proj_id
通常被设置为任意字符,比如'K'
。ftok()
函数使用关于已命名文件的信息(比如inode
编号等)和proj_id
来为msgget()
生成一个可能唯一的键(key
)。要使用相同队列的程序必须生成相同的密钥,因此它们必须将相同的参数传递给ftok()
。
系统函数msgget()
的原型如下所示:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
该函数用于创建一个新的消息队列,或者是获取一个现有的消息队列。如果该函数调用成功,返回消息队列的msgid
作为进程的唯一标识符;失败则返回-1
。第一个参数key
是一个IPC``key
,可以由ftok()
函数生成,标识唯一的消息队列。
第二个参数flag
是创建方式(IPC_CREAT
和IPC_EXCL
)。
单独使用IPC_CREAT
表示如果请求的资源已经存在,则直接获得创建IPC资源的应用程序;如果不是,则创建新的IPC资源。
IPC_CREAT
与IPC_EXCL
一起使用,以指示用于创建IPC资源的应用程序。如果请求的资源不存在,则创建一个新的IPC资源;如果它已经存在,则返回-1
。
单独使用IPC_EXCL
没有任何意义。它的存在是为了将其与IPC_CREAT
一起使用,以确保新创建的资源是可用的。
系统函数msgsnd()
用于向消息队列添加数据,其函数原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
该函数成功返回0,失败返回-1。对于该函数的几个参数,详细描述如下:① msqid
msgget()函数返回的消息队列标识符。
② msgp
用于指向要发送的消息的指针。
③ msgsz
msg
指向的消息长度,消息缓冲区结构中mtext
的大小,不包括数据类型。
注意:msgsnd()
和msgrcv()
函数中的第二个参数(分别是msgp
和msg_ptr
)的数据类型均为struct msgbuf
,其数据类型声明如下(/include/uapi/linux/msg.h
文件):
/* message buffer for msgsnd and msgrcv calls */
struct msgbuf {
__kernel_long_t mtype; /* type of message */
char mtext[1]; /* message text */
};
④ msgflg
指定如何发送消息的标志。比如:msgflg``=``IPC_NOWAIT
, 则表示如果消息不能立即发送,不阻塞进程,返回-1
,并设置错误码errno
(用户区)为EAGAIN
。如果不设置标志,则使用值0
。
系统函数msgrcv()
用于从消息队列中读取消息,该函数原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
对于该函数中的5
个参数,详细解释说明如下:① msqid
该参数表示函数msgget()
返回的消息队列标识符。
② msgp
指向要接收的消息的指针。
③ msgsz
msgp
指向的消息的长度,消息缓冲区结构中mtext
的大小,不包括数据类型。正如上面4.3
节所描述,msgp
的数据类型是struct``msgbuf
。
④ msgtyp
我们希望阅读的消息类型。可能是以下情况之一:
i. msgtyp``=``0
, 将返回队列上的第一条消息。ii. msgtyp``>``0
(正整数), 队列上类型(mtype
,即struct``msgbuf
中成员mtype
)等于此整数的第一条消息(除非msgflg
中设置了特定标志,请参见下文⑤)。iii. msgtyp``<``0
(负整数), 队列上类型小于或等于此整数绝对值的第一条消息。
如果msgtyp
为零,则检索第一条消息,而不管其类型是什么。该值可由接收进程用于指定消息选择。总的来说,msgflg
设置为0
。
⑤ msgflg
该参数控制函数的行为,以下任何标志的逻辑“或”组合。
msgflg``=``IPC_NOWAIT
,队列没有可读消息不等待,返回ENOMSG
错误。msgflg``=``MSG_EXCEPT
,如果消息类型参数为正整数,则返回类型不等于给定整数的第一条消息。msgflg``=``MSG_NOERROR
,当消息大小超过msgze
时被截断。
“这个参数依然是控制函数行为的标志,取值可以是:0,表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。如果一个client 正在等待消息的时候队列被删除,EIDRM 就会被返回。如果进程在阻塞等待过程中收到了系统的中断信号,EINTR 就会被返回。MSG_NOERROR,如果函数取得的消息长度大于msgsz,将只返回msgsz 长度的信息,剩下的部分被丢弃了。如果不指定这个参数,E2BIG 将被返回,而消息则留在队列中不被取出。当消息从队列内取出后,相应的消息就从队列中删除了。
”
在Linux内核的5.4.3
版本中,msgflg
支持以下选项参数:
//// 声明于/include/uapi/linux/msg.h
/* msgrcv options */
#define MSG_NOERROR 010000 /* no error if message is too big */
#define MSG_EXCEPT 020000 /* recv any msg except of specified type.*/
#define MSG_COPY 040000 /* copy (not remove) all queue messages */
特别注意:当msgtype``>``0
且 msgflg``=``MSG_EXCEPT
时,接收类型不等于msgtype
的第一条消息。
该函数调用成功时,返回实际放入接收缓冲区msgp
中的字符数。失败返回-1
。
系统函数msgctl()
用于消息队列控制功能,它不仅仅有删除消息队列的作用。其函数原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
该函数的各参数详细如下描述:
① msqid
msgget()
函数返回的消息队列标识符。
② cmd
指定要在消息队列上执行的命令,其具体如下:
所以如果我们执行删除操作,我们可以将cmd
设置为IPC_RMID
。对于IPC_INFO
命令(Linux系统特有),其对应的msginof
数据类型声明如下(/include/uapi/linux/msg.h
文件):
/* buffer for msgctl calls IPC_INFO, MSG_INFO */
struct msginfo {
int msgpool;
int msgmap;
int msgmax;
int msgmnb;
int msgmni;
int msgssz;
int msgtql;
unsigned short msgseg;
};
③ buf
如果选项删除队列,则第3
个参数buf
传NULL
。参数buf
的数据类型是 struct``msqid_ds
,其数据类型声明如下所示:
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
与消息队列相关的shell命令有两个,分别是:ipcs
和 ipcrm
。它们分别用来查看消息队列以及删除消息队列;除此之外,这两个命令还可以用于共享内存、信号量。这些都将在后面的相关文章中详细进行讲解,本文只着重说明与消息队列相关的选项。
使用ipcs -q
可以查看当前消息队列中的消息情况。
如下图所示,一开始内核中没有消息队列,然后我们启动write
(实现写消息队列的demo
)应用程序,之后向该消息队列中写入HELLO
字段串,之后再次使用ipcs -q
便可查看到消息队列向的key
和msqid
以及消息数量等信息。
现在使用ipcrm
将该消息队列中的所有消息删掉,如下所示,可以选择根据key
或msqid
去删除,这里选项的是根据msqid
进行删除。
· 实例一该实例中,生产者不断地向消息队列中写入用户终端输入的消息,直到输入“over
”字符串后,结束该过程。
生产者代码:
/// 向消息队列写入消息:sender.c文件; gcc sender.c -o sender
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/msg.h>
struct msgbuf{
int mtype;
char mtext[128];
};
int main(int argc,char *argv[])
{
int msgid;
char buf[128] = {0};
key_t key = 11111;
struct msgbuf sndbuf;
if((msgid = msgget(key, 0666 | IPC_CREAT)) < 0)
{
perror("msgget");
exit(-1);
}
while(1)
{
printf("Enter some text: ");
memset(&sndbuf, 0x00, sizeof(sndbuf));
sndbuf.mtype = 1;
memset(buf, 0x00, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
strncpy(sndbuf.mtext, buf, strlen(buf));
if(msgsnd(msgid,(void *)&sndbuf, sizeof(sndbuf), 0) < 0 )
{
perror("msgsnd");
exit(-1);
}
}
return 0;
}
消费者代码:
/// 从消息队列中读取消息;receiver.c文件;gcc receiver.c -o receiver
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/msg.h>
#include <ctype.h>
struct msgbuf{
int mtype;
char mtext[128];
};
int main(int argc,char *argv[])
{
int msgid;
char buf[128] = {0};
key_t key = 11111;
struct msgbuf rcvbuf;
if((msgid = msgget(key, 0666 | IPC_CREAT)) < 0)
{
perror("msgget");
exit(-1);
}
while(1)
{
memset(&rcvbuf, 0x00, sizeof(rcvbuf));
if(msgrcv(msgid, (void *)&rcvbuf, sizeof(rcvbuf), 0, 0) < 0)
{
perror("msgrcv");
exit(-1);
}
// 移除掉fgets函数读取到的换行符。
if(isspace(rcvbuf.mtext[strlen(rcvbuf.mtext) - 1]))
{
rcvbuf.mtext[strlen(rcvbuf.mtext) - 1] = '\0';
}
printf("rcv msg[%s]\n", rcvbuf.mtext);
if(!strncmp(rcvbuf.mtext, "over", 4))
{
puts("over cycle.");
break;
}
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
分别打开两个shell终端,一个纸箱sender
进程,另外一个纸箱receiver
进程。在sender
进程中,用户不断从终端输入消息,而receiver
终端则会将所从消息队列中获取到的消息打印到出来,直到读取到字符“over
”则从内核中删除该消息队列。
序号 | 命令 | 说明 |
---|---|---|
1 | IPC_STAT | 获取此消息队列的msqid_ds结构,并将其存储在buf所指向的结构中 |
2 | IPC_SET | 将参数buf中的一些字段复制到消息队列msqid_ds结构中的相应字段。此命令只能由两个用户执行,一个是其有效用户ID等于msg_perm.cuid和另一个msg_perm.uid。另一个是超级用户root。 |
3 | IPC_RMID | 从系统中删除消息队列和消息队列的所有数据。删除立即生效。 |
4 | IPC_INFO(Linux特有) | 返回有关buf指向的结构中的系统范围消息队列限制和参数的信息。该结构为msginfo型。 |
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/DxoXt6yfLHD1niGvt6RKow
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。