6.消息队列
6.1 IPC对象
除了最原始的进程间通信方式信号、无名管道和有名管道外,还有三种进程间通信方式,这三种方式称之为IPC对象: 消息队列、共享内存、信号灯集。
IPC对象也是在内核空间开辟区域,每一种IPC对象创建好之后都会将其设置为全局,并且会给其分配一个编号,只要找到唯一的这个编号就可以进行通信,所以不相关的进程可以通过IPC对象通信。
IPC对象创建好之后,会在当前系统中可见,只要不删除或者不关闭系统,就会一直存在。
查看系统中已经创建的IPC
| ipcs |
| ipcs -q |
| ipcs -m |
| ipcs -s |
| |

6.2 消息队列概述
- 消息队列中的消息是有类型的
- 消息队列中的消息是有格式的
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取
- 消息队列允许一个或多个进程向它写入或者读取消息
- 与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
- 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
- 只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。
SystemV提供的 IPC 通信机制需要一个key 值,通过key植就可在系统内获得一个唯一的消息队列标识符
key 值可以人为指定,也可以通过 ftok
函数获得。
6.2.1 ftok函数
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| key_t ftok(const char *pathname, int proj id); |
| 功能:获得项目相关的唯一的 IPC 键值; |
| 参数: |
| 路径名pathname: |
| prej_id:项目 ID,非0整数(只有低 8 位有效),范围 0 ~ 127; |
| 返回值: |
| 成功:返回 key 值; |
| 失败:返回 -1 |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <stdlib.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| key_t mykey; |
| if ((mykey = ftok(".", 100))== -1) |
| { |
| perror("fail to ftok"); |
| exit(1); |
| } |
| printf("mykey = %#x\n",mykey); |
| |
| return 0; |
| } |
输出结果
6.3 消息队列的操作
6.3.1 创建消息队列 - msgget() 函数
| #include<sys/types.h> |
| #include<sys/ipc.h> |
| #include<sys/msg.h> |
| |
| int msgget(key_t key, int msgflg); |
| 功能:创建一个消息队列,得到消息队列的id; |
| 参数: |
| key:键值,唯一的键值确定唯一的消息队列方法 |
| 1:任意指定一个数 |
| 2:使用ftok函数获取键值; |
| msgflg: 消息队列的访问权限,一般设置为 IPC_CREAT|IPC_EXCL|0777 或者 IPC_CREAT 0777 |
| 返回值: |
| 成功:消息队列的id; |
| 失败:返回 -1; |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <sys/msg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| key_t mykey; |
| if ((mykey = ftok(".", 100)) == -1) |
| { |
| perror("fail to ftok"); |
| exit(1); |
| } |
| printf("mykey = %#x\n", mykey); |
| |
| int msqid; |
| if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1) |
| { |
| perror("fail to msgget"); |
| exit(1); |
| } |
| |
| printf("msqid = %d\n", msqid); |
| |
| return 0; |
| } |
输出结果
| mykey = 0x640516aa |
| msqid = 0 |

6.3.2 发送信息 - msgsnd() 函数
| #include<sys/msg. h> |
| int msgsnd(int msgid, const void *msgp,size_t msgsz, int msgflg); |
| 功能: |
| 将新消息添加到消息队列; |
| 参数: |
| msgid:消息队列的标识符; |
| msgp:待发送消息结构体的地址,需要写入的数据,自己定义结构体; |
| struct struct_name{ |
| long mtype; |
| char mtext[128]; |
| } |
| msgsz: 消息正文的字节数,不包括消息编号的长度; |
| msgflg: 函数的控制属性 |
| 0: msgsnd,调用阻塞直到条件满足为止。 |
| IPC_NOWAIT: 非阻塞,若消息没有立即发送则调用该函数的进程会立即返回。 |
| 返回值: |
| 成功: 0; |
| 失败: 返回-1。 |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <sys/msg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| typedef struct |
| { |
| |
| long msg_type; |
| char msg_text[128]; |
| } MSG; |
| |
| |
| #define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long)) |
| |
| int main(int argc, char const *argv[]) |
| { |
| key_t mykey; |
| if ((mykey = ftok(".", 100)) == -1) |
| { |
| perror("fail to ftok"); |
| exit(1); |
| } |
| printf("mykey = %#x\n", mykey); |
| |
| int msqid; |
| if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1) |
| { |
| perror("fail to msgget"); |
| exit(1); |
| } |
| |
| printf("msqid = %d\n", msqid); |
| system("ipcs -q"); |
| |
| |
| MSG msg1 = {1, "hello world"}; |
| MSG msg2 = {1, "i love c"}; |
| MSG msg3 = {1, "i love hello kitty"}; |
| if ((msgsnd(msqid, &msg1, MSGTEXT_SIZE, 0)) == -1) |
| { |
| perror("fail to send message"); |
| } |
| system("ipcs -q"); |
| if ((msgsnd(msqid, &msg2, MSGTEXT_SIZE, 0)) == -1) |
| { |
| perror("fail to send message"); |
| } |
| if ((msgsnd(msqid, &msg3, MSGTEXT_SIZE, 0)) == -1) |
| { |
| perror("fail to send message"); |
| } |
| system("ipcs -q"); |
| return 0; |
| } |
输出结果
| mykey = 0x640516aa |
| msqid = 2 |
| |
| --------- 消息队列 ----------- |
| 键 msqid 拥有者 权限 已用字节数 消息 |
| 0x640516aa 2 spider 666 0 0 |
| |
| |
| --------- 消息队列 ----------- |
| 键 msqid 拥有者 权限 已用字节数 消息 |
| 0x640516aa 2 spider 666 128 1 |
| |
| |
| --------- 消息队列 ----------- |
| 键 msqid 拥有者 权限 已用字节数 消息 |
| 0x640516aa 2 spider 666 384 3 |

注意:消息队列结束后,消息仍然存在于消息队列中,而如果是管道,就会消失。
6.3.3 接收数据 - msgrev() 函数
| #include <sys/msg.h> |
| ssize_t msgrcv(int msgid, void *msgp,size_t msgsz, long msgtyp, int msgflg) |
| 功能: 从标识符为msgid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除; |
| 参数: |
| msgid: 消息队列的标识符,代表要从哪个消息列中获取消息。 |
| msgp: 存放消息结构体的地址。 |
| msgsz: 消息正文的字节数。 |
| msgtyp: 消息的类型、可以有以下几种类型 |
| msgtyp = 0: 返回队列中的第一个消息 |
| msgtyp > 0: 返回队列中消息类型为msgtyp的消息 |
| msgtyp < 0: 返回队列中消息类型值小于或等于 msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。 |
| 注意: |
| 若消息队列中有多种类型的消息,msscv,获取消息的时候按消息类型获取,不是先进先出的。 |
| 在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。 |
| msgflg:函数的控制属性 |
| 0: msgrev 调用阻塞直到接收消息成功为止。 |
| MSG_NOERROR:若返回的消息字节数比 nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。 |
| IPC NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1. |
| 返回值: |
| 成功: 返回读取消息的长度; |
| 失败: 返回-1 |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <sys/msg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| typedef struct |
| { |
| |
| long msg_type; |
| char msg_text[128]; |
| } MSG; |
| |
| |
| #define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long)) |
| |
| int main(int argc, char const *argv[]) |
| { |
| key_t mykey; |
| if ((mykey = ftok(".", 100)) == -1) |
| { |
| perror("fail to ftok"); |
| exit(1); |
| } |
| printf("mykey = %#x\n", mykey); |
| |
| int msqid; |
| if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1) |
| { |
| perror("fail to msgget"); |
| exit(1); |
| } |
| |
| printf("msqid = %d\n", msqid); |
| system("ipcs -q"); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| MSG msg; |
| |
| |
| |
| if (msgrcv(msqid, &msg, MSGTEXT_SIZE, 1, 0) == -1) |
| { |
| perror("fail to msgrcv"); |
| exit(1); |
| } |
| printf("recv_msg = %s\n", msg.msg_text); |
| system("ipcs -q"); |
| |
| return 0; |
| } |
输出结果
| mykey = 0x640516aa |
| msqid = 2 |
| |
| --------- 消息队列 ----------- |
| 键 msqid 拥有者 权限 已用字节数 消息 |
| 0x640516aa 2 spider 666 384 3 |
| |
| recv_msg = hello world |
| |
| --------- 消息队列 ----------- |
| 键 msqid 拥有者 权限 已用字节数 消息 |
| 0x640516aa 2 spider 666 256 2 |
6.3.4 消息队列的控制 - msgctl() 函数
| #include<sys/msg. h> |
| int msgetl(int msqid int cmd, struct msqid_ds *buf) |
| 功能: 对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列; |
| 参数: |
| msqid: 消息队列的标识符 |
| cmd: 函数功能的控制 |
| IPC_RMID: 删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关数据结构; |
| IPC_STAT: 将 msqid 相关的数据结构中各个元素的当前值存入到由buf指向的结构中; |
| IPC_SET: 将 msqid 相关的数据结构中的元素设置为由 buf指向的结构中的对应值。 |
| buf: msqid_ds,数据类型的地址,用来存放或更改消息队列的属性 |
| |
| 返回值: |
| 成功:返回0 |
| 失败:返回-1 |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <sys/msg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| key_t key; |
| if ((key = ftok(".", 20)) == -1) |
| { |
| perror("fail to ftok"); |
| exit(1); |
| } |
| printf("key = %#x\n", key); |
| |
| int msgid; |
| if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1) |
| { |
| perror("fail to msgget"); |
| exit(1); |
| } |
| |
| printf("msgid = %d", msgid); |
| system("ipcs -q"); |
| |
| |
| if (msgctl(msgid, IPC_RMID, NULL) == -1) |
| { |
| perror("fail to msgctl"); |
| exit(1); |
| } |
| |
| system("ipcs -q"); |
| return 0; |
| } |
输出结果
| key = 0x140516aa |
| |
| --------- 消息队列 ----------- |
| 键 msqid 拥有者 权限 已用字节数 消息 |
| 0x640516aa 2 spider 666 256 2 |
| 0x140516aa 3 spider 777 0 0 |
| |
| |
| --------- 消息队列 ----------- |
| 键 msqid 拥有者 权限 已用字节数 消息 |
| 0x640516aa 2 spider 666 256 2 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步