当前位置: 首页 > news >正文

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

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,但默认情况下可能是按发送顺序接收的,除非指定了不同的消息类型优先级。

一、消息队列的核心特性

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

二、消息队列的管理与操作

消息队列

  1. 创建或获取消息队列

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

#include <sys/msg.h>

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

使用 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 非阻塞发送)。
  1. 接收消息

使用 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| 的最小类型消息。
  1. 控制消息队列

使用 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:队列中当前未读的消息数量。

四、消息队列的底层实现

  1. 内核数据结构
    • struct msg_queue:内核为每个消息队列维护的结构体,包含消息链表、权限、统计信息等。
    • 消息存储:消息以链表形式存储,按类型和到达时间排序。
  2. 同步与阻塞机制
    • 发送阻塞:若队列满(受系统限制 msgmnb),发送进程阻塞或返回错误。
    • 接收阻塞:若队列空或指定类型消息不存在,接收进程阻塞或返回错误。
  3. 系统限制
    • 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;
}

运行步骤:

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

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

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

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

相关文章:

  • Unity实现高性能多实例RTSP|RTMP播放器技术实践
  • 【Spring+MyBatis】留言墙的实现
  • SOCKET建立简单的tcp服务端与客户端通信
  • 【动态路由】系统web url整合系列【springcloud-gateway实现】【不改hosts文件版】组件一:多个Eureka路由过滤器
  • 【深度解析】图解Deepseek-V3模型架构-混合专家模型(MoE)
  • 海尔小红书年度规划方案拆解
  • rabbitmq五种模式的总结——附java-se实现(详细)
  • Task03:Ollama API 的使用
  • Spring AI集成DeepSeek:三步搞定Java智能应用
  • AI芯片NVDA、AVGO、MRVL、AMD估值分析
  • 【Python】01-基础
  • 使用JavaScript实现深浅拷贝
  • 升级 SpringBoot3 全项目讲解 — 别再使用 Optional 了,请使用 Jspecify 来替代它
  • 怎么能在互联网上找到某个专业的专业资料?
  • 在 UniApp 项目中设置多语言
  • 2025.2.16机器学习笔记:TimeGan文献阅读
  • c#模拟鼠标点击左键
  • 【开源项目】图床工具Easyimage保姆级搭建
  • Windows编程:用 VS2019 编写C语言程序
  • 103-《茶靡花》
  • 香港特区立法会通过条例草案便利外地公司迁册来港
  • 腾讯一季度营收增长13%,马化腾:战略性的AI投入将带来长期回报
  • 江西贵溪:铜板上雕出的国潮美学
  • 美国务卿鲁比奥将前往土耳其参加俄乌会谈
  • 中国-拉共体论坛第四届部长级会议北京宣言
  • “75万买299元路由器”事件进展:重庆市纪委等三部门联合介入调查