1、消息队列
消息队列是 Linux 系统中一种基于内核的、面向数据块(消息)的异步 IPC 机制,本质是内核维护的一个消息链表,进程通过 API 向队列中添加 / 读取消息,实现进程间通信。
1.1 IPC对象
- 消息队列、共享内存、信号量(信号灯)都属于System V IPC 对象,内核会为每个 IPC 对象维护一个独立的内核数据结构(而非 “内存文件”);
- 每个 IPC 对象有唯一的IPC 标识符(ID)(如消息队列 ID、共享内存 ID),且会被内核持久化(进程退出后仍存在,需手动删除)。
1.2 键值
- 键值(key_t)是 IPC 对象的全局唯一标识,用于在多个进程间定位同一个 IPC 对象(ID 是内核内部标识,键值是用户层标识);
- IPC对象的名称,一个键值对应一个IPC对象,而每个IPC对象只能是三种类型中的一种:(要么是消息队列,要么是共享内存,要么是信号量)
- 键值的生成方式:
- 手动指定固定值(如key_t key = 0x123456;);
- 通过ftok()函数生成(推荐,避免键值冲突)。
1.3 进程间通信(消息队列)
多个进程要通过消息队列通信,必须满足:
使用同一消息队列 → 访问同一个System V IPC对象 → 使用同一个键值(key) → (若用ftok)使用同一个pathname和proj_id。
1.4 常用命令(管理IPC对象)
| ipcs | 查看所有 System V IPC 对象的信息(消息队列、共享内存、信号量) |
| ipcs -q | 只查看消息队列(-m:共享内存,-s:信号量) |
| ipcs -l | 可查看 IPC 对象的系统限制(如最大消息队列数、单队列最大字节数)。 |
| ipcrm -Q <KEY> | 根据键值(key)删除消息队列(-M:共享内存,-S:信号量) |
| ipcrm -q <MSGID/SHMID/SEMID> | 根据消息队列 ID 删除消息队列(-m:共享内存 ID,-s:信号量 ID) |
1.5 核心函数接口
1.5.1 ftok(生成键值)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
- 功能:根据文件路径和项目 ID 生成唯一的 IPC 键值;
- 参数:
- pathname:必须是存在且可访问的文件路径(如"/tmp/ipc_test");
- proj_id:项目 ID(仅低 8 位有效,通常传 1 个字符如'a',范围 1-255);
- 返回值:
- 成功:返回key_t类型的键值;
- 失败:返回-1(需检查errno,如文件不存在、权限不足);
- 关键:同一pathname + 同一proj_id → 生成相同键值;任意一个不同 → 键值不同。
1.5.2 msgget(创建 / 获取消息队列)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
- 功能:创建新的消息队列,或获取已存在的消息队列 ID;
- 参数:
- key:IPC 键值(由ftok生成或手动指定);
- msgflg:标志位(组合使用):
- IPC_CREAT:若键值对应的队列不存在则创建,存在则获取;
- IPC_EXCL:与IPC_CREAT配合使用,若队列已存在则报错(确保创建新队列);
- 权限位:如0664(同文件权限,所有者可读可写,组可读,其他可读);
- 返回值:
- 成功:返回消息队列 ID(非负整数);
- 失败:返回-1(如权限不足、队列已存在且用了IPC_EXCL);
- 示例:msgget(key, IPC_CREAT | 0664);(创建 / 获取队列,权限 0664)。
1.5.3 mesgsnd(发送消息到队列)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- 功能:向指定消息队列发送一条消息;
- 参数:
- msqid:消息队列 ID(msgget返回值);
- msgp:指向自定义消息结构体的指针(必须以long mtype开头):
// 自定义消息结构(mtype必须>0,mtext长度可自定义)
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */
char mtext[1024]; /* 消息数据(可自定义长度) */
};
- msgsz:消息数据部分的长度(即mtext的有效长度,不含mtype);
- msgflg:发送标志:
- 0:默认,队列满时阻塞;
- IPC_NOWAIT:队列满时不阻塞,直接返回错误;
- 返回值:成功返回0,失败返回-1(如队列不存在、权限不足、消息类型≤0)。
1.5.4 msgrcv(从队列接收消息)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
- 功能:从消息队列中读取指定类型的消息;
- 参数:
- msqid:消息队列 ID;
- msgp:存放接收消息的缓冲区(同msgsnd的msgbuf结构);
- msgsz:缓冲区mtext的最大长度(超出部分会被截断,且不报错);
- msgtyp:指定接收的消息类型(核心参数):
- msgtyp = 0:读取队列中第一个消息(不区分类型);
- msgtyp > 0:读取队列中第一个类型为 msgtyp的消息;
- msgtyp < 0:读取队列中类型≤|msgtyp|的最小类型的消息;
- msgflg:接收标志:
- 0:默认,队列无匹配消息时阻塞;
- IPC_NOWAIT:无匹配消息时不阻塞,直接返回错误;
- MSG_NOERROR:消息长度超过msgsz时,截断数据且不报错(默认会报错);
- 返回值:
- 成功:返回实际读取的mtext字节数;
- 失败:返回-1(如队列不存在、权限不足)。
1.5.5 msgctl(控制消息队列)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
- 功能:对消息队列执行控制操作(查询状态、修改属性、删除队列);
- 参数:
- msqid:消息队列 ID;
- cmd:控制命令(核心):
- IPC_STAT:获取队列的状态信息(存入buf);
- IPC_SET:修改队列的属性(从buf读取);
- IPC_RMID:删除消息队列(最常用,删除后队列立即消失);
- buf:
- 执行IPC_STAT/IPC_SET时:指向msqid_ds结构体(存放队列状态);
- 执行IPC_RMID时:传NULL即可;
- 返回值:成功返回0,失败返回-1(如队列不存在、无删除权限);
- 示例:msgctl(msqid, IPC_RMID, NULL);(删除消息队列)。
2、共享内存
共享内存是 Linux 中最高效的进程间通信方式—— 内核在物理内存中开辟一块连续区域,多个进程将这块内存映射到自己的虚拟地址空间,进程可直接读写该内存(无需数据拷贝),通信效率远超管道 / 消息队列。
核心优势:其他 IPC 方式(如消息队列)需要 2 次数据拷贝(用户态→内核态→用户态),共享内存仅需映射,数据直接在用户态读写,无拷贝开销。
2.1 核心函数接口
2.1.1 ftok(同消息队列)
2.1.2 shmget(创建 / 获取共享内存)
- 函数原型:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
- 功能:创建新的共享内存段,或获取已存在的共享内存 ID;
- 参数:
- key:IPC 键值(ftok生成或手动指定,如IPC_PRIVATE表示创建私有共享内存);
- size:共享内存的大小(字节数,建议按页大小对齐,如 4096、8192,系统页大小用getpagesize()获取)或者终端:getconf PAGESIZE;
- shmflg:标志位(组合使用):
- IPC_CREAT:不存在则创建,存在则获取;
- IPC_EXCL:与IPC_CREAT配合,若已存在则报错(确保创建新内存段);
- 权限位:如0664(同文件权限,控制进程对共享内存的读写);
- 返回值:
- 成功:返回共享内存 ID(非负整数);
- 失败:返回-1(如权限不足、内存大小非法)。
2.1.3 shmat(映射共享内存到进程地址空间)
- 函数原型:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
- 功能:将内核中的共享内存段映射到当前进程的虚拟地址空间,返回可直接读写的指针;
- 参数:
- shmid:共享内存 ID(shmget返回值);
- shmaddr:指定映射到进程的虚拟地址:
- NULL:推荐用法,让系统自动选择合适的地址(避免地址冲突);
- 非空:手动指定地址(需确保地址合法,一般不推荐);
- shmflg:映射属性:
- 0:默认,读写权限(需共享内存创建时的权限允许);
- SHM_RDONLY:只读权限(即使共享内存有写权限,也只能读);
- 返回值:
- 成功:返回映射后的内存起始地址(void*,需强转为对应类型使用);
- 失败:返回(void*)-1(注意不是-1,需用(void*)-1判断)。
2.1.4 shmdt(解除共享内存映射)
- 函数原型:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
- 功能:将共享内存从当前进程的地址空间中解除映射(仅断开连接,不删除共享内存);
- 参数:shmaddr:shmat返回的映射地址;
- 返回值:成功返回0,失败返回-1;
- 关键:进程退出前必须解除映射,否则可能导致内存泄漏。
2.1.5 shmctl(控制共享内存)
- 函数原型:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 功能:对共享内存执行查询、修改、删除等操作;
- 参数:
- shmid:共享内存 ID;
- cmd:控制命令(核心):
- IPC_STAT:读取共享内存属性(存入buf,同消息队列);
- IPC_SET:修改共享内存属性(从buf读取,仅能改权限、所有者等);
- IPC_RMID:删除共享内存段(最常用,删除后内核释放该内存);
- SHM_LOCK:锁定共享内存(禁止换出到交换分区,需 root 权限);
- SHM_UNLOCK:解锁共享内存;
- buf:
- IPC_STAT/IPC_SET:指向shmid_ds结构体(存放属性);
- IPC_RMID:传NULL即可;
- 返回值:成功返回0,失败返回-1。
3、信号灯
System V信号灯是多个信号量组成的数组,用于实现进程间的同步 / 互斥(如保护共享内存的读写);单个信号量是一个计数器,通过P/V操作(减 1 / 加 1)控制资源访问。
3.1 函数接口
3.1.1 ftok(同消息队列)
3.1.2 semget(创建 / 获取信号量集)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
- 功能:创建新的信号量集,或获取已存在的信号量集 ID;
- 参数:
- key:IPC 键值(ftok生成或IPC_PRIVATE);
- nsems:信号量集中的信号量个数(创建时必须指定,获取时可填 0);
- semflg:标志位(组合使用):
- IPC_CREAT:不存在则创建,存在则获取;
- IPC_EXCL:与IPC_CREAT配合,若已存在则报错(确保创建新集);
- 权限位:如0664(控制进程对信号量集的操作权限);
- 返回值:
- 成功:返回信号量集 ID(非负整数);
- 失败:返回-1(如权限不足、nsems 非法)。
3.1.3 semctl(控制信号量集 / 单个信号量)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, …);
- 功能:对信号量集 / 单个信号量执行初始化、删除、查询等操作;
- 参数:
- semid:信号量集 ID(semget返回值);
- semnum:信号量集中的单个信号量下标(从 0 开始,操作整个集时可填 0);
- cmd:核心命令:
命令功能 SETVAL 设置单个信号量的初始值(需配合第四个参数union semun) GETVAL 获取单个信号量的当前值(返回值为信号量值) IPC_RMID 删除整个信号量集(semnum 无效,第四个参数传 NULL,也可省略) SETALL 设置信号量集中所有信号量的初始值(第四个参数传数组) GETALL 获取信号量集中所有信号量的当前值(第四个参数传数组) - 可变参数(…):需传入union semun结构体(必须手动定义,系统不默认提供):
// 必须手动定义该联合体!
union semun {
int val; // SETVAL:单个信号量的初始值
struct semid_ds *buf; // IPC_STAT/IPC_SET:信号量集属性
unsigned short *array; // GETALL/SETALL:所有信号量的值数组
struct seminfo *__buf; // IPC_INFO:系统限制(Linux特有)
};
- 返回值:
- 成功:SETVAL/IPC_RMID返回 0,GETVAL返回信号量当前值;
- 失败:返回-1。
3.1.4 semop(执行 P/V 操作,核心)
- 函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
- 功能:对信号量集执行一组原子操作(P 操作 / V 操作),确保操作不被中断;
- 参数:
- semid:信号量集 ID;
- sops:指向struct sembuf结构体数组的指针(定义操作列表):
struct sembuf {
unsigned short sem_num; // 操作的信号量下标(从0开始)
short sem_op; // 操作类型(核心):
// -1:P操作(申请资源,信号量减1,无资源则阻塞)
// +1:V操作(释放资源,信号量加1)
// 0:等待信号量值变为0
short sem_flg; // 操作标志:
// 0:默认,无资源则阻塞
// IPC_NOWAIT:无资源则不阻塞,直接返回错误
// SEM_UNDO:进程退出时自动撤销本次操作(避免死锁)
}; - nsops:操作列表的长度(即sops数组的元素个数);
- 返回值:成功返回0,失败返回-1;
- 核心特性:semop的所有操作是原子性的 —— 要么全部执行,要么全部不执行(避免竞态条件)。
网硕互联帮助中心




评论前必须登录!
注册