System消息队列

📅 2026/7/13 21:17:34
System消息队列
System V 消息队列生命周期随内核和共享内存一样调用后删除命令后不会立刻销毁仅做删除标记所有当前挂载使用队列的进程全部退出后内核才真正释放队列和里面所有消息收发逻辑进程 A 发送调用msgsnd()封装type1 自定义数据内核在队列链表尾部新增红色 node 节点。进程 B 接收调用msgrcv()指定type2链表无 type2 消息 → B 进程阻塞等待等到内核从链表中寻找第一条 type2 的绿色消息取出唤醒B队列中 type1 的红色消息会继续留在链表不会被读取。也可以B的type设置为1则就能和A进行通信结论 1带类型的数据块传输消息队列的核心特色每条消息绑定一个整型type标记。进程 A 发送type1的红色消息进程 B 可以指定只接收type2的绿色消息跳过队列里其他类型数据对比管道管道只能按先进先出顺序读无法分类筛选。结论2操作系统统一管理内核必须维护管理结构struct msqid_ds队列总控结构体权限、消息总数、字节上限、创建者 PID 等struct node单条消息链表节点保存 type、数据、下一条消息指针所有消息、权限、统计信息全部由 OS 内核管理进程只通过系统调用收发数据。结论3多进程共享同一队列的关键 —— key和共享内存逻辑完全一致双方用相同路径 项目 ID 调用ftok()生成同一个key通过msgget(key, flag)打开 / 创建队列内核依靠key匹配保证不同进程映射到内核里同一个消息队列对象。msgget 创建或打开#include sys/types.h #include sys/ipc.h #include sys/msg.h int msgget(key_t key, int msgflg);参数msgflg创建标志 权限和 shmget 规则完全一致IPC_CREATkey 不存在则新建队列存在则直接打开IPC_CREAT | IPC_EXCL不存在才创建已存在直接报错防止重复创建返回值成功返回非负整数msqid消息队列操作句柄供 msgsnd/msgrcv/msgctl 使用失败返回-1eg// 创建队列不存在则新建权限0666存在就直接打开 int msqid msgget(key, IPC_CREAT | 0666); // 只打开已有队列不创建 int msqid msgget(key,0); // 强制创建 int msqid msgget(key, IPC_CREAT | IPC_EXCL | 0666);msgctl —— 消息队列控制函数删队列、改权限、查信息int msgctl(int msqid, int cmd, struct msqid_ds *buf);参数解析msqidmsgget 返回的队列 idcmd控制命令3 个最常用IPC_STAT 【读取队列内核信息】IPC_SET 【修改队列权限 / 所有者】IPC_RMID 【删除消息队列最常用bufstruct msqid_ds结构体存放队列内核信息根据 cmd 决定是否使用。//IPC_STAT 【读取队列内核信息】 struct msqid_ds info; msgctl(msqid, IPC_STAT, info); printf(队列权限%ho\n, info.msg_perm.mode); printf(当前消息条数%ld\n, info.msg_qnum); //IPC_SET 【修改队列权限 / 所有者】 msgctl(msqid, IPC_STAT, info); info.msg_perm.mode 0644; // 修改权限 msgctl(msqid, IPC_SET, info); // IPC_RMID 【删除消息队列最常用】 msgctl(msqid, IPC_RMID, NULL); 调用后不会立刻销毁仅做删除标记所有当前挂载使用队列的进程全部退出后内核才真正释放队列和里面所有消息内核描述队列msgsnd 发送 msgrcv 接收所有收发都必须自定义消息缓冲区第一个成员必须是 long mtype0后面自定义数据struct MsgBuf { long mtype; // 消息类型正数核心过滤标识 char mtext[128];// 自定义承载数据 };msgsnd发送消息到队列尾部#include sys/msg.h int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);参数解析msqidmsgget 返回的消息值msgp自定义MsgBuf结构体指针msgsz仅数据长度不含 mtype即sizeof(buf.mtext)msgflg阻塞控制标志0默认阻塞。队列满时发送进程休眠直到队列腾出空间IPC_NOWAIT非阻塞。队列满直接返回 - 1不等待egstruct MsgBuf buf; buf.mtype 1; // 设置消息类型 strcpy(buf.mtext, hello); // 阻塞发送数据长度是字符串长度 msgsnd(msqid, buf, strlen(buf.mtext), 0);返回值成功返回0失败-1并设置errnomsgrcv从队列读取匹配类型消息#include sys/msg.h ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);参数解析msqidmsgget返回值msgp接收消息的缓冲区结构体指针msgsz缓冲区数据区最大可容纳字节mtext 数组长度msgtyp消息过滤核心参数分 3 种规则msgflg阻塞控制0无匹配消息时阻塞休眠IPC_NOWAIT无匹配消息直接返回 - 1不阻塞MSG_NOERROR消息数据超过 msgsz 时截断不报错不加则返回 - 1msgtyp 三种读取规则msgtyp 0精准匹配遍历链表取出第一条 mtype msgtyp的消息其他类型消息保留在队列。例msgtyp2 → 只读 type2 的消息type1 完全忽略。msgtyp 0先进先出不筛选永远取出链表最头部第一条消息不管 type 是多少。msgtyp 0极少使用取出队列中mtype ≤ abs(msgtyp)里最小 type 的第一条消息。返回值成功返回实际拷贝到mtext缓冲区的字节数失败返回-1同步设置errnoegstruct MsgBuf buf; // 阻塞读取type2的消息缓冲区最大128字节 ssize_t n msgrcv(msqid, buf, sizeof(buf.mtext), 2, 0); if(n 0) { printf(收到数据%s\n, buf.mtext); }查看和删除消息队列ipcs -q查看ipcrm -q msqid删除怎么通过同步和互斥来保护共享资源临界资源被保护起来的共享资源叫做临界资源同一时刻只能一个进程使用临界区 vs 非临界区进程代码分成两部分临界区访问临界资源的那一段代码重点结论保护资源的本质不是锁住数据本身而是锁住访问数据的这段代码。非临界区不碰共享资源的代码多个进程可以随便并行执行不需要加锁互斥互斥定义任何时刻只允许一个执行流访问临界资源同一临界区同一时间只能有一个进程进去。就比如区ATM取钱每次只能一个人互斥的底层硬性要求操作必须具备原子性加锁 / 申请锁操作必须具备原子性要么完整执行、要么完全不执行否则可能加锁操作过程到一半就会被打断进而竞争失效。进入区加锁→ 临界区操作共享资源→ 退出区解锁→ 非临界区同步在互斥基础上规定多个执行流访问资源的先后顺序。同步只管控时序但无法阻止多执行流同时进入临界区没有互斥同步逻辑再完善也会数据错乱。2者结合进入区① 先执行互斥加锁原子操作保证独占临界区② 判断同步条件不满足访问条件就阻塞等待比如缓冲区满生产者等待临界区独占读写共享缓冲区退出区① 同步唤醒等待的另一个执行流生产者写完唤醒消费者② 互斥解锁释放资源访问权限非临界区无关逻辑自由并发。即先用互斥先保证同一时间只有一个进程进入临界区如果有先后执行要求再叠加同步控制访问顺序