《深入理解Linux内核》 第十九章:深入理解 Linux 进程通信机制(Process Communication)
关键词:IPC、信号、管道、FIFO、消息队列、信号量、共享内存、套接字、内核对象、同步机制
一、进程通信概述
1.1 为什么需要进程通信
在 Linux 系统中,进程是资源隔离的基本单位,彼此间通常无法直接访问彼此的地址空间。因此需要一套机制,使得多个进程之间可以:
- 交换数据;
- 同步行为;
- 发送通知;
- 共享资源。
这些功能由 Linux IPC(Inter-Process Communication)子系统实现。
1.2 IPC 分类
类型 | 描述 |
---|---|
信号 | 最基本的异步通知机制 |
管道/FIFO | 字节流通信机制,面向数据流 |
消息队列 | 面向结构化消息,先进先出 |
信号量 | 同步与互斥,典型用于资源控制 |
共享内存 | 多进程映射同一段内存,效率最高 |
套接字 | 网络与本地通信统一抽象,支持多协议 |
二、信号(Signal)
2.1 概述
信号是进程间最早被引入的通信机制,本质上是一个异步事件通知。
示例信号 | 描述 |
---|---|
SIGINT | 中断(Ctrl+C) |
SIGTERM | 终止进程请求 |
SIGKILL | 无条件终止进程(不可捕获) |
SIGCHLD | 子进程结束通知父进程 |
2.2 信号相关系统调用
kill(pid, sig)
:向指定进程或进程组发送信号;signal(sig, handler)
:设置信号处理函数;sigaction()
:更强大的信号控制;sigprocmask()
:阻塞/允许某些信号;sigqueue()
:带参数的信号发送。
2.3 内核实现
- 每个进程结构
task_struct
中包含sigpending
和signal
字段; - 信号通过
do_signal()
派发; - 某些信号是不可忽略/不可屏蔽的(如
SIGKILL
); - Linux 通过实时信号(
SIGRTMIN
~SIGRTMAX
)支持有序队列与附加参数。
三、管道与 FIFO
3.1 管道(pipe)
管道是最基本的 IPC 数据流机制,数据在两个进程间以 FIFO 形式流动。
int pipe(int pipefd[2]); // pipefd[0]=read, pipefd[1]=write
特点:
- 半双工(单向通信);
- 父子进程间常见用途;
- 基于内核中的
pipe_inode_info
结构实现; - 使用缓冲区环形队列实现数据传输。
3.2 命名管道(FIFO)
可跨进程、不限于亲缘关系:
mkfifo /tmp/myfifo
特点:
- 是一种特殊文件;
- 可在 shell 中使用
< /tmp/myfifo
、> /tmp/myfifo
。
3.3 内核实现
- 管道是内核中一种特殊的字符设备;
- 每个 pipe 被表示为一个 inode;
- 内核用
struct pipe_inode_info
管理缓冲区、读写端等。
四、消息队列(Message Queues)
4.1 概述
消息队列允许进程以“消息”为单位进行通信,每条消息是结构化数据(类型 + 数据内容)。
int msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
4.2 内核数据结构
struct msg_queue {struct list_head q_messages;...
};
- 所有消息链入
q_messages
; - 支持按类型匹配接收;
- 消息大小有限制(
msgmax
)。
4.3 特点
- 支持异步发送、同步接收;
- 按优先级接收;
- 属于 System V IPC 之一;
- 支持权限与 quota 控制。
五、信号量(Semaphores)
5.1 概述
信号量提供一种同步机制,用于控制多个进程对共享资源的访问,支持阻塞和非阻塞操作。
int semget(key_t key, int nsems, int semflg);
int semop(int semid, struct sembuf *sops, size_t nsops);
- 每个信号量集合由
semid
索引; semop()
支持加锁/解锁操作(P/V操作);- 使用计数表示资源状态。
5.2 内核结构
struct sem_array {struct sem *sem_base;...
};
- Linux 信号量集支持原子操作;
- 支持 undo 机制,在进程终止时自动释放资源;
- 属于 System V IPC 范畴,现代内核中逐渐被 futex 和 pthread 替代。
六、共享内存(Shared Memory)
6.1 概述
多个进程将一段物理内存映射到各自虚拟地址空间,实现高效通信。
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
特点:
- 速度最快的 IPC;
- 通常配合信号量/互斥锁使用;
- 属于 System V IPC;
6.2 内核实现
- 使用
shmid_kernel
描述共享段; - 实质上是内核为各进程映射同一段物理页;
- 页表项被多个进程共享;
- 写时复制(COW)策略在此无效。
七、套接字通信(Socket)
7.1 套接字种类
类型 | 描述 |
---|---|
UNIX 域套接字 | 仅限本机通信,文件系统路径寻址 |
INET 套接字 | 网络通信,使用 IP 与端口 |
STREAM | 面向连接,类似 TCP |
DGRAM | 无连接,类似 UDP |
7.2 创建通信过程
int socket(int domain, int type, int protocol);
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);
int connect(int sockfd, struct sockaddr *addr, socklen_t addrlen);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- socket 通信是最通用的进程通信方式;
- 在内核中通过
sock
结构表示; - 数据缓冲使用
sk_buff
结构管理。
八、Linux IPC 统一接口
Linux 提供 /proc/sysvipc/
系统接口与 ipcs
工具来统一查看 IPC 对象:
ipcs -m # 共享内存
ipcs -q # 消息队列
ipcs -s # 信号量
可以使用 ipcrm
删除对象。
九、IPC 命名空间与隔离(Namespace)
9.1 IPC Namespace
每个 IPC 对象(shm、sem、msg)都可以在命名空间中隔离,容器技术(如 Docker)广泛使用。
- 创建 IPC namespace:
unshare --ipc
- 每个命名空间有独立的 IPC 资源空间;
- 安全、稳定、互不干扰。
十、内核源码参考路径
文件路径 | 作用描述 |
---|---|
kernel/signal.c | 信号实现与分发 |
ipc/msg.c | 消息队列实现 |
ipc/sem.c | 信号量实现 |
ipc/shm.c | 共享内存实现 |
fs/pipe.c | 管道与 FIFO 实现 |
net/unix/af_unix.c | UNIX 域 socket 实现 |
include/linux/ipc_namespace.h | IPC 命名空间 |
include/linux/shm.h | 共享内存头文件 |
十一、小结
- Linux 提供多种 IPC 机制,各有优缺点与适用场景;
- 信号适合简单事件通知;
- 管道与 FIFO 适合流式数据传输;
- 消息队列支持结构化数据传递;
- 信号量用于同步资源控制;
- 共享内存效率最高,但需额外同步手段;
- 套接字最通用,跨主机通信也适用;
- IPC 命名空间提升系统隔离性与安全性。