
大家好,接下来的一段时间我将开始学习野火的Linux 系统课程并将学习到的干货逐步更新到我的CSDN博客中。没时间刷课的同学可以把我的博客喂给AI 突击一下。
System‑V IPC 三大组件:消息队列、信号量、共享内存;
共享内存是速度最快的 IPC 方式。
一、简介
高效率传输大量数据原理 :多个进程把同一块内核内存映射到各自进程虚拟地址空间,进程直接读写这块内存,没有内核缓冲区拷贝。
⚠️共享内存本身不带同步互斥机制!多进程并发读写必须搭配 System‑V 信号量来保护临界区。
二、使用流程
ftok():生成唯一key,用于跨进程定位同一个共享内存对象shmget():创建 / 获取共享内存内核对象,得到shmidshmat():映射,把内核共享内存挂载到当前进程虚拟地址,得到可用内存指针- 读写共享内存(直接指针操作)
shmdt():解除映射,断开进程和这块内存的关联(进程退出也会自动 detach,但最好手动写)shmctl(IPC_RMID):删除内核中的共享内存对象
注意:shmdt 只是脱离映射,不是删除共享内存;IPC_RMID 才是销毁内核对象。
三、函数详解
1. shmget --- 创建 / 获取共享内存 ID
功能:创建共享内存段,或者打开已存在的共享内存,返回 shmid
int shmget(key_t key, size_t size, int shmflg);
- 参数
key:ftok 生成的键值size:共享内存字节大小;内核分配时会向上对齐到内存页大小shmflg:标志IPC_CREAT:不存在则创建;存在直接打开- mode:权限,如
0664
- 返回
- 成功:共享内存 ID**(shmid)**
- 失败:
-1
2. shmat --- 映射共享内存
功能:将内核共享内存映射到当前进程虚拟地址空间
void *shmat(int shmid, const void *shmaddr, int shmflg);
- 参数
shmid:共享内存 IDshmaddr:指定映射地址;shmflgSHM_RDONLY:只读映射0:可读可写
- 返回
- 成功:共享内存起始虚拟地址
- 失败:
-1
重点:拿到返回指针后,直接像普通数组一样读写内存。
3. shmdt --- 解除映射
功能:断开当前进程与共享内存段的映射关系
int shmdt(const void *shmaddr);
- 参数:
shmaddr:shmat 返回的映射首地址 - 返回
- 成功:
0 - 失败:
-1
- 成功:
仅解除当前进程的映射,不会删除内核里的共享内存对象;只要还有进程 attach,内存就不会销毁。
4. shmctl --- 控制共享内存
功能:获取 / 修改共享内存属性,删除共享内存对象
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 参数
shmid:共享内存 IDcmd命令IPC_STAT:读取共享内存属性,存入 bufIPC_SET:修改共享内存属性IPC_RMID:标记删除共享内存;不会立刻销毁,等所有进程都 shmdt 之后才真正释放
buf:属性结构体指针,IPC_RMID 时填 NULL 即可
- 返回
- 成功:视 cmd 而定
- 失败:
-1
四、实战演练
cpp
#include <sys/types.h>
#include "sem.h"
#define DELAY_TIME 3
int main(void)
{
pid_t result;
int sem_id;
int shm_id;
char *addr;
sem_id = semget((key_t)6666, 1, 0666 | IPC_CREAT); /* 创建一个信号量 */
shm_id = shmget((key_t)7777, 1024, 0666 | IPC_CREAT); /* 创建一个共享内存对象 */
/* 初始化信号量 */
init_sem(sem_id, 0);
/* 调用 fork() 函数 */
result = fork();
if (result == -1) {
perror("Fork\n");
} else if (result == 0) { /* 返回值为 0 代表子进程 */
printf("Child process will wait for some seconds...\n");
sleep(DELAY_TIME);
/* 映射共享内存 */
addr = shmat(shm_id, NULL, 0);
if (addr == (void *)-1) {
printf("shmat111 error!");
exit(-1);
}
/* 设置共享内存中的内容 */
memcpy(addr, "helloworld", 11);
printf("the child process is running...\r\n");
sem_v(sem_id);
} else { /* 返回值大于 0 代表父进程 */
sem_p(sem_id);
printf("the father process is running...\r\n");
/* 映射共享内存地址 */
addr = shmat(shm_id, NULL, 0);
if (addr == (void *)-1) {
printf("shmat222 error!");
exit(-1);
}
printf("shared memory string:%s\r\n", addr);
/* 接触共享内存映射 */
shmdt(addr);
}
exit(0);
}
五、关键考点总结
-
速度优势:无内存拷贝,进程直接读写物理内存,三大 IPC 里最快。
-
同步问题:共享内存本身没有互斥保护,多进程读写必须配合信号量。
-
生命周期:内核对象,进程退出不会自动删除,必须
shmctl(IPC_RMID)。 -
删除机制:IPC_RMID 只是打删除标记;所有进程 detach 之后,内核才真正释放内存。
ipcs -m # 查看系统所有共享内存
ipcrm -m shmid # 删除指定共享内存
| IPC | 核心 API | 本质 | 特点 |
|---|---|---|---|
| 消息队列 | msgget/msgsnd/msgrcv/msgctl | 内核消息链表 | 有数据拷贝;自带消息边界;自带阻塞 |
| 信号量 | semget/semctl/semop | 内核计数器 | 不传输数据;用于互斥、同步 |
| 共享内存 | shmget/shmat/shmdt/shmctl | 内核内存段 | 无拷贝速度最快;无同步,必须搭配信号量 |