I — 进程间通信
进程间通信(IPC)是不同进程之间交换数据的方式。由于进程有独立的虚拟地址空间,它们不能直接访问彼此的内存,需要内核提供通信机制。
IPC 机制对比
| 机制 | 数据单元 | 特点 | 场景 |
|---|---|---|---|
| 管道 (Pipe) | 字节流 | 单向、父子进程常见 | Shell 管道 (`ls |
| 命名管道 (FIFO) | 字节流 | 有文件系统名称、任意两进程 | 独立进程间通信 |
| 共享内存 | 字节块 | 最快(无数据拷贝)、需要同步 | 高性能场景 |
| 消息队列 | 消息(有边界) | 异步消息传递、OS 维护生命周期 | 多进程任务分发 |
| 信号 (Signal) | 整数信号编号 | 异步通知、少量信息 | 进程终止(SIGKILL)、定时(SIGALRM) |
| 套接字 (Socket) | 字节流 / 数据报 | 跨网络、跨主机 | 网络服务 |
| mmap 文件映射 | 字节块 | 文件→内存映射 | 大文件读写、数据库 |
管道 (Anonymous Pipe)
管道由 pipe() 调用创建,返回两个文件描述符:fd[0](读端)和 fd[1](写端)。内容写入后,从另一端顺序读取。
int fd[2];
pipe(fd);
if (fork() == 0) {
close(fd[0]); // 子进程关闭读端
write(fd[1], "hello", 5);
} else {
close(fd[1]); // 父进程关闭写端
read(fd[0], buf, 5);
}内核中的管道使用环形缓冲区实现,默认容量 64KB。管道是半双工——数据只能单向流动;需要双向通信则创建两个管道。
命名管道 (FIFO)
与管道相同,但在文件系统中有一个名字(通过 mkfifo 建立),任意两个知道该名字的进程都可以打开并使用。生命期独立于创建它的进程。
共享内存
共享内存是最快的 IPC 方式——数据不需要在内核和用户空间之间拷贝,双方直接读写同一物理页:
// 创建共享内存段
int shm_id = shmget(key, 4096, IPC_CREAT | 0666);
char *addr = shmat(shm_id, NULL, 0);
// 现在 addr 可被创建进程和所有附加进程同时访问缺点:需要额外的同步机制(如信号量)来防止竞争条件。共享内存本身只是一块被映射到两个进程的物理页,不提供任何同步保证。
信号 (Signal)
信号是异步通知,携带的信息极小(只有信号编号)。进程可以注册信号处理函数,或选择忽略/阻塞信号。
| 信号 | 编号 | 含义 |
|---|---|---|
| SIGINT | 2 | Ctrl+C |
| SIGKILL | 9 | 强制终止(不能被捕获或忽略) |
| SIGSEGV | 11 | 段错误(非法内存访问) |
| SIGALRM | 14 | 定时器到期 |
| SIGCHLD | 17 | 子进程状态变化 |
本章与其他模块的链接
- 管道中的环形缓冲区 → 循环队列
- 信号与中断 → A_操作系统概述 > 中断与异常
- 共享内存与
mmap→ F_内存管理 > 虚拟内存