主页 > IT业界  > 

【进程与线程】SystemVIPC:消息队列(MessageQueue)

【进程与线程】SystemVIPC:消息队列(MessageQueue)
System V 消息队列(Message Queue)

消息队列(Message Queue)是 System V IPC 机制中的一种 进程间通信(IPC) 方式,允许进程通过发送和接收格式化消息进行通信。

System V IPC:消息队列(Message Queue) 1> 先进先出的队列 因为消息队列允许根据消息类型非顺序读取:例如,可以指定接收特定类型的消息, 2> 消息队列的格式: 消息类型 + 消息正文 使用ipcs命令时显示的列: key msqid owner perms used-bytes messages 已经使用的字节数 当前消息队列中未读的消息数 key:创建消息队列时使用的键值。 msqid:消息队列的标识符。 owner:拥有者。 perms:权限。 used-bytes:已使用的字节数。 messages:队列中的消息数量。 覆盖消息队列的基本操作: 创建或获取消息队列(msgget) 发送消息(msgsnd) 接收消息(msgrcv) 控制消息队列(msgctl)

消息队列是先进先出的队列,实际中消息队列允许根据消息类型进行读取,不完全是FIFO。消息的格式为:消息类型加消息正文 ——> 例如,在C语言中如何使用struct来定义消息,其中必须包含一个长整型的消息类型字段,然后是消息正文。

消息队列的特点:内核持久性,即使进程结束,消息队列仍然存在,除非显式删除;权限管理,类似文件权限,如何设置读写权限。消息队列的优点在于可以异步通信,数据有结构,缺点是需要内核操作,效率不如共享内存,且存在系统限制(如最大消息数、消息大小限制)。“先进先出的队列”:因为消息队列允许根据消息类型非顺序读取。例如,可以指定接收特定类型的消息,或者按类型优先级读取。因此,消息队列并不是严格的FIFO,但默认情况下可能是按发送顺序接收的,除非指定了不同的消息类型优先级。

一、消息队列的核心特性 消息结构 每条消息包含两个部分: 消息类型(long mtype):用于标识消息的优先级或类别。消息正文(用户自定义数据):任意长度的数据字段。 消息结构示例: struct msgbuf { long mtype; // 消息类型(必须 > 0) char mtext[100]; // 消息正文(长度可自定义) }; 先进先出(FIFO)与优先级读取 默认FIFO:消息按发送顺序排队。按类型读取:接收时可指定消息类型,实现优先级或选择性读取。 内核持久性 消息队列由内核维护,生命周期独立于进程。需显式删除(msgctl(..., IPC_RMID)),否则持续存在直至系统重启。 二、消息队列的管理与操作

创建或获取消息队列

使用 msgget() 创建或获取消息队列标识符:

#include <sys/msg.h> key_t key = ftok("/tmp", 'A'); // 生成唯一键值 int msqid = msgget(key, IPC_CREAT | 0666); key:唯一标识消息队列的键值(通过 ftok() 生成)。msgflg:权限标志(如 IPC_CREAT | 0666)。 发送消息

使用 msgsnd() 向队列发送消息:

struct msgbuf msg; msg.mtype = 1; // 消息类型 strcpy(msg.mtext, "Hello Message Queue"); msgsnd(msqid, &msg, sizeof(msg.mtext), 0); // 阻塞发送 msqid:消息队列标识符。msgp:指向消息结构体的指针。msgsz:消息正文长度(不包含 mtype)。msgflg:标志(如 IPC_NOWAIT 非阻塞发送)。 接收消息

使用 msgrcv() 从队列接收消息:

struct msgbuf msg; ssize_t bytes = msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0); // 接收类型为1的消息 printf("Received: %s\n", msg.mtext); msgtyp:指定接收的消息类型: =0:接收队列中第一条消息。>0:接收类型等于 msgtyp 的第一条消息。<0:接收类型 ≤ |msgtyp| 的最小类型消息。 控制消息队列

使用 msgctl() 管理消息队列:

msgctl(msqid, IPC_RMID, NULL); // 删除消息队列 cmd:控制命令(如 IPC_STAT 获取状态,IPC_RMID 删除队列)。 三、消息队列的状态查看

通过 ipcs -q 命令查看系统中的消息队列信息:

ipcs -q ------ Message Queues -------- key msqid owner perms used-bytes messages 0x61005a3d 12345 user 666 1024 5 key:消息队列的唯一键值。msqid:消息队列的标识符。owner:队列拥有者。perms:权限(八进制格式,如 666 表示所有用户可读写)。used-bytes:队列中所有消息占用的总字节数。messages:队列中当前未读的消息数量。 四、消息队列的底层实现 内核数据结构 struct msg_queue:内核为每个消息队列维护的结构体,包含消息链表、权限、统计信息等。消息存储:消息以链表形式存储,按类型和到达时间排序。 同步与阻塞机制 发送阻塞:若队列满(受系统限制 msgmnb),发送进程阻塞或返回错误。接收阻塞:若队列空或指定类型消息不存在,接收进程阻塞或返回错误。 系统限制 msgmax:单条消息的最大长度(通过 sysctl kernel.msgmax 查看)。msgmnb:队列的最大字节数。msgmni:系统允许的最大消息队列数。

代码示例:

#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int main() { //1- 获得键值key key_t key = ftok("/",0x3); if(key == -1) { perror("ftok"); return -1; } //2- 利用key 创建/打开消息队列 //int msgget(key_t key, int msgflg); int msgid = msgget(key,IPC_CREAT | 0666); if(msgid == -1) { perror("msgget"); return -1; } printf("key = %#x,msgid = %d\n",key,msgid); //查看消息队列的属性 struct msqid_ds msg; int ret = msgctl(msgid,IPC_STAT,&msg); if(ret == -1) { perror("msgctl"); return -1; } #if 0 struct msqid_ds { struct ipc_perm msg_perm; /* Ownership and permissions */ time_t msg_stime; /* Time of last msgsnd(2) 消息队队列最后发送的时间 */ time_t msg_rtime; /* Time of last msgrcv(2) 消息队队列最后接受的时间*/ time_t msg_ctime; /* Time of last change消息队队列最后更改的时间 */ unsigned long __msg_cbytes; /* Current number of bytes in queue (nonstandard) 消息队列当前的字节数 */ msgqnum_t msg_qnum; /* Current number of messages in queue消息队列当前的消息数 */ msglen_t msg_qbytes; /* Maximum number of bytes allowed in queue 消队列允许的最大字节数*/ pid_t msg_lspid; /* PID of last msgsnd(2) 最后调用msgsnd函数的进程PID*/ pid_t msg_lrpid; /* PID of last msgrcv(2) 最后调用msgrcv函数的进程PID*/ }; #endif printf("__msg_cbytes = %lu\n",msg.__msg_cbytes); printf("msg_qnum = %ld\n",msg.msg_qnum); printf("msg_qbytes = %lu\n",msg.msg_qbytes); return 0; } 五、代码示例:进程间通信

发送进程(sender.c)

#include <sys/msg.h> #include <stdio.h> #include <string.h> struct msgbuf { long mtype; char mtext[100]; }; int main() { key_t key = ftok("/tmp", 'A'); int msqid = msgget(key, IPC_CREAT | 0666); struct msgbuf msg; msg.mtype = 1; strcpy(msg.mtext, "Hello from Sender!"); msgsnd(msqid, &msg, sizeof(msg.mtext), 0); printf("Sender sent: %s\n", msg.mtext); return 0; }

接收进程(receiver.c)

#include <sys/msg.h> #include <stdio.h> struct msgbuf { long mtype; char mtext[100]; }; int main() { key_t key = ftok("/tmp", 'A'); int msqid = msgget(key, 0666); struct msgbuf msg; msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0); printf("Receiver received: %s\n", msg.mtext); msgctl(msqid, IPC_RMID, NULL); // 删除消息队列 return 0; }

运行步骤:

编译并运行发送进程: gcc sender.c -o sender ./sender 编译并运行接收进程: gcc receiver.c -o receiver ./receiver 优点缺点支持消息类型和优先级内核操作,性能低于共享内存数据有结构,易解析系统限制可能影响扩展性异步通信,无需实时同步生命周期需显式管理

消息队列适用的场景:1. 结构化数据通信:需要按类型处理消息的场景(如任务分发)。2. 跨进程异步通知:生产者-消费者模型。3. 替代简单网络通信:同一主机内进程间的高效通信。

以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。

我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。 感谢!

标签:

【进程与线程】SystemVIPC:消息队列(MessageQueue)由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【进程与线程】SystemVIPC:消息队列(MessageQueue)