7 消息队列
7.1 概念说明
这里讨论的消息队列是操作系统层面的 IPC 机制,不同于应用组件中的消息中间件。简单来说,就是内核维护的一个队列结构,用于进程间传递数据。
7.2 工作原理
消息队列属于系统级 IPC,由用户设置一个共享的 key 值(A、B 进程均可见),双方约定通过该 key 访问同一个队列。内核中会维护相应的描述结构体。

消息队列和共享内存一样,都是系统级的 IPC。如果数据节点只包含数据,操作系统如何区分归属?必须保证数据块是有类型的。因此,消息队列是一种向另一个进程发送有类型数据块的方式。
核心系统调用如下:

7.3 接口函数
涉及的主要接口包括创建、发送、接收及删除等,具体参数结构参考下图:


7.4 常用命令
查看消息队列状态:

删除消息队列:

8 信号量
在共享内存场景中,由于缺乏保护机制,多进程并发访问容易导致数据竞争。信号量正是为了解决这一问题而引入的同步互斥工具。
注:此处侧重理论理解,线程部分的信号量将在后续章节展开。
8.1 概念补充
学习管道时可能对'共享资源'感知不强,但信号量能让我们深刻理解多进程如何访问同一份资源。
- 共享资源:多个执行流(进程)都能看到的公共资源。
- 临界资源:被保护起来、一次只允许一个执行流访问的资源。
8.1.1 互斥与同步
- 互斥:任何时刻只允许一个执行流访问资源(如 ATM 取钱、上厕所)。
- 同步:多个进程按特定顺序执行(如一个进程等待另一个进程完成)。
互斥 + 同步 + 共享资源 = 临界资源
8.1.2 程序员视角
无论是同步还是互斥,最终都落实到代码层面。例如父子进程同时向 stdout 打印内容,由于 Linux 下'一切皆文件',显示器作为设备文件属于共享资源且未被保护,会导致输出错乱。解决此类问题需要引入同步与互斥机制。

8.1.3 临界区
访问公共资源的代码段称为【临界区】,不访问的称为【非临界区】。进入临界区前加锁,离开时解锁,以此保护临界资源。

8.1.4 思维导图

8.2 信号量的三个核心问题
学习信号量可借鉴'是什么、为什么、怎么办'的思路。
8.2.1 信号量是什么?
教材常将其译为'信号灯'。它本质上是一种实现同步和互斥的技术,适用于公共资源的整体或局部访问场景。

8.2.2 本质理解
信号量是一个计数器,衡量临界资源的数量。想象成 int count,初始值为资源总数。

8.2.3 实例类比
以电影院为例,座位数即资源上限。买票相当于申请信号量(P 操作),入座即访问资源。若票卖完,后续进程需等待。这体现了对共享资源的预定机制。

伪代码逻辑如下:
// 简化示意
if (count > 0) {
count--;
// 进入临界区
} else {
// 等待
}
8.2.4 极端场景
若放映厅只有一个座位,竞争将更激烈,信号量的计数作用更为关键。

8.2.5 思维导图

8.2.6 为什么需要信号量?
根本原因是解决多进程访问共享资源时的并发读写冲突。

8.2.7 如何实现?
需要了解相关接口:创建 (semget)、操作 (semop)、控制 (semctl)。
- 创建信号量集:
semget允许创建信号量集合。 - 删除信号量:使用
semctl指定IPC_RMID。 - 获取信息:
semctl配合IPC_STAT可获取创建时间等元数据。

信号量本质是计数器,操作通常涉及 +1 或 -1。

sembuf 结构体是 semop() 调用的核心,包含三个成员:
sem_num:指定操作哪个信号量(索引)。sem_op:定义增减值或等待条件。sem_flg:控制阻塞行为与自动撤销。
这三个成员共同决定了信号量如何修改或等待,是实现同步互斥的基础。
以下是一段典型的 P 操作示例代码:
#include <sys/sem.h>
struct sembuf sb;
// 假设已通过 semget() 获得信号量集标识符 semid
sb.sem_num = 0; // 第 1 个信号量
sb.sem_op = -1; // 减少 1(P 操作)
sb.sem_flg = SEM_UNDO; // 进程退出时自动还原
if (semop(semid, &sb, 1) == -1) {
perror("semop");
} else {
// 成功获取资源,进入临界区
}
注意:SEM_UNDO 的回滚机制较复杂,常规开发中了解即可。此外,信号量的创建与空间开辟是分开的步骤。
8.2.8 实现流程总结


