文章目录
- 1.概念
- 1.0 IPC
- 1.1 什么是消息队列
- 1.2 消息队列工作机制
- 1.3 消息队列与其他进程通信机制的比较:
- 2.使用System-V版
- 2.1 用户消息缓冲区
- 2.2 创建消息队列`msgget`
- 2.3 添加消息到消息队列`msgsend`
- 2.4 从消息队列读取消息、
- 2.5 消息队列的控制函数`msgctrl`
- 2.6 msqid_ds
- 4. SYSTEM_V和POSIX的差异
https://zhuanlan.zhihu.com/p/268389190
1.概念
IPC
的意思是“ 进程间通信机制”,Linux内核有三种常用IPC对象可以拿来做进程间通信–消息队列,共享内存,信号量。这三种IPC对象在Linux内核中都以链表的形式存储,它们都有特定的ID来标识(消息队列标识符msqid、共享内存标识符shmid,信号量标识符semid)。
每个 IPC 对象都对应一个ipc_perm
结构体, ipc_perm
结构体中保留了该ipc对象对应的一些权限信息:
struct ipc_perm {
key_t __key; /* Key supplied to semget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsignedshort mode; /* Read/write permission.*/
unsignedshort __seq; /* Sequence number */
};
1.0 IPC
1.1 什么是消息队列
消息队列称为报文队列也叫做信箱,是linux的一种通信机制这种通信机制传递的数据具有某种结构,而不是简单的字节流。
- 消息队列的本质是内核提供的链表,内核基于这个链表,实现了一个数据结构。内核又基于此提供了从一个进程向另一个进程发送一块数据的方法
- 向消息队列写数据,实际上是向这个数据结构中插入一个新节点;从消息队列读数据,实际上是从这个数据结构中删除一个节点。
- 消息队列也有管道一样的不足,就是每个数据块的最大长度是有限的,系统上全体队列的最大值也有一个上限。
1.2 消息队列工作机制
1.3 消息队列与其他进程通信机制的比较:
与信号量相比,消息队列可以承载更多的通信数据。
与管道的默认接收相比,消息队列可以让接收进程有选择地接收通信数据,还可以设置接收的优先级。当使用消息队列的进程终止时,消息队列不会自动删除。但所有引用管道的进程终止时,管道会自动删除。
与共享内存相比,共享内存的速度更快,因为对共享内存的处理不经过内核调用,而消息队列需要经过内核调用。但是在多核系统上,为了避免产生高速缓存一致性问题,更推荐使用消息队列。
2.使用System-V版
2.1 用户消息缓冲区
无论是发送进程还是接收进程,都需要在进程空间中用消息缓冲区来暂存消息,该消息缓冲区的结构定义如下
struct msgbuf{
long mtype;
char mtext[1];
};
- 可以通过mtype来区分数据类型,通过判断mtype,是否为需要接收的数据
- mtext[1]为存放消息的正文数组,可以根据消息的大小定义该数组的长度
2.2 创建消息队列msgget
函数原型如下
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数:
key
: 某个消息队列的名字,通过宏值定义,或者使用ftok函数生成。
msgflg
:由九个权限标志构成,用法和创建文件时使用的mode模式标志是一样的,这里举两个来说明
IPC_CREAT
如果消息队列对象不存在,则创建之,否则则进行打开操作
IPC_EXCL
如果消息对象不存在则创建之,否则产生一个错误并返回
返回值:
成功返回一个非负整数,即该消息队列的标识码;
失败则返回-1
2.3 添加消息到消息队列msgsend
函数原型如下:
int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
参数:
msgid: 由msgget函数返回的消息队列标识码
msg_ptr:是一个指针,指针指向准备发送的消息缓冲区
msg_sz:是msg_ptr指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
如:
msgflg = IPC_NOWAIT 表示队列满不等待,返回EAGAIN错误
返回值:
成功返回0
失败则返回-1
2.4 从消息队列读取消息、
函数原型如下:
int msgrcv(int msgid, void *msg_ptr, size_t msgsz,
long int msgtype, int msgflg);
参数:
msgid: 由msgget函数返回的消息队列标识码
msg_ptr:是一个指针,指针指向准备接收的消息,
msgsz:是msg_ptr指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
msgtype:它可以实现接收优先级的简单形式
msgtype=0返回队列第一条信息
msgtype>0返回队列第一条类型等于msgtype的消息
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
注意
msgtype>0且msgflg=MSC_EXCEPT,接收类型不等于msgtype的第一条消息
返回值:
成功返回实际放到接收缓冲区里去的字符个数
失败,则返回-1
2.5 消息队列的控制函数msgctrl
函数原型:
int msgctl(int msqid, int command, strcut msqid_ds *buf);
参数:
- msqid: 由msgget函数返回的消息队列标识码
- command:是将要采取的动作,(有三个可取值)分别如下:
命令 | 说明 |
---|---|
IPC_STAT | 获取该消息队列的信息,获取到的信息会储存在结构体msqid_ds类型的buf中 |
IPC_SET | 在进程有足够权限时,把消息队列的属性值设置为msqid_ds数据结构中给出的值 |
IPC_RMID | 删除消息队列 |
IPC_INFO | 获得系统对消息队列做的限制 |
注意若选择删除队列,第三个参数传NULL;
返回值:
如果操作成功,返回“0”;如果失败,则返回“-1”
2.6 msqid_ds
常用于SYSTEM_V版的函数,这个结构体用于控制一个消息队列
struct msqid_ds{
struct ipc_perm msg_perm;
time_t msg_stime; //发送到队列的最后一个消息的时间戳
time_t msg_rtime; //从队列中获取的最后一个消息的时间戳
time_t msg_ctime; //对队列进行最后一次变动的时间戳
unsigned long __msg_cbytes; //在队列上所驻留的字节总数
msgqnum_t msg_qnum; //当前队列中消息的数量
msglen_t msg_qbytes; //当前队列允许的最大bytes数
pid_t msg_lspid; //发送最后一个消息的进程PID
pid_t msg_lrpid; //接收最后一个消息的进程PID
};
4. SYSTEM_V和POSIX的差异
System V和POSIX的主要差异在于它们的设计目标、实现方式、性能、可靠性、以及应用场景。
- 设计目标和实现方式:
- System V IPC(Inter-Process Communication,进程间通信)主要由贝尔实验室和BSD开发,侧重于进程之间的通信手段的改进,基于内核实现。它提供了信号量、消息队列和共享内存等机制,用于在进程间进行通信和同步1。
- POSIX(Portable Operating System Interface,可移植操作系统接口)则是由IEEE制定的标准,旨在为运行在不同操作系统上的软件提供统一的接口。POSIX的实现由不同的操作系统内核开发人员完成,旨在提高系统的可移植性1。
- 性能和可靠性:
- 在性能方面,POSIX在无竞争条件下不会陷入内核,而System V无论何时都要陷入内核,因此在性能上稍逊一筹1。
- 在可靠性方面,POSIX的sem_wait函数成功获取信号量后,如果进程意外终止,将无法释放信号量。而System V提供了SEM_UNDO选项来解决这个问题,因此后者更加可靠1。
- 应用场景:
- System V的应用更为广泛,尤其是在一些较旧的操作系统或未实现POSIX标准的系统中。然而,考虑到可移植性,POSIX成为了一个趋势,尤其在需要跨平台兼容的应用中更为常见1。
- 在进程间通信和同步方面,POSIX的使用似乎更为普遍,尽管在共享内存方面,System V仍然占据主流12。
总的来说,System V和POSIX各有优势和适用场景。System V提供了更广泛的兼容性和对旧系统的支持,而POSIX则以其标准化的接口和高可移植性受到青睐。选择使用哪种机制取决于具体的应用需求和目标平台的特性12。