linux 进程间通信

一、IPC 总览

1. IPC 目的

  • 数据传输:一个进程把数据发给另一个进程。
  • 资源共享:多个进程共享同一资源。
  • 通知事件:通知另一个或一组进程发生了某事件。
  • 进程控制:一个进程控制另一个进程,如 Debug 进程。

2. IPC 发展

  • 管道
  • System V IPC
  • POSIX IPC

3. IPC 分类

  • 管道
    • 匿名管道:pipe
    • 命名管道:FIFO、mkfifo
  • System V IPC
    • 消息队列
    • 共享内存
    • 信号量
  • POSIX IPC
    • 消息队列、共享内存、信号量
    • 互斥量、条件变量、读写锁

4. 本节重点

  • 掌握匿名/命名管道的原理与操作。
  • 编写进程池。
  • 掌握共享内存。
  • 了解消息队列、信号量。
  • 理解内核如何管理 IPC 资源。
  • 了解 C/C++ 中回调、多态在进程池中的体现。

二、管道

1. 匿名管道 pipe

函数原型:

c 复制代码
int pipe(int fd[2]);
  • fd[0]:读端
  • fd[1]:写端
  • 成功返回 0,失败返回错误码。

本质:

  • 管道是内核中的一块缓冲区。
  • 从文件描述符角度看,管道也像文件,符合"Linux 一切皆文件"。
  • 读写管道使用 read、write、close。

fork 后共享管道:

  • 父进程创建管道。
  • fork 后子进程继承 fd[0]、fd[1]。
  • 通常父进程关闭读端,子进程关闭写端,实现单向通信。
  • 双向通信需要两个管道。

管道特点:

  • 只能用于有亲缘关系的进程,通常是父子进程。
  • 半双工,数据单向流动。
  • 提供流式服务,没有消息边界。
  • 生命周期随进程,进程退出管道释放。
  • 内核会对管道操作进行同步与互斥。
  • 写端全关,read 返回 0。
  • 读端全关,write 会产生 SIGPIPE。

2. 管道读写规则

没有数据可读:

  • O_NONBLOCK 关闭:read 阻塞。
  • O_NONBLOCK 开启:read 返回 -1,errno = EAGAIN。

管道满:

  • O_NONBLOCK 关闭:write 阻塞,直到有进程读走数据。
  • O_NONBLOCK 开启:write 返回 -1,errno = EAGAIN。

关闭情况:

  • 所有写端关闭:read 返回 0。
  • 所有读端关闭:write 触发 SIGPIPE,可能导致进程退出。

原子性:

  • 写入数据量 <= PIPE_BUF:Linux 保证写入原子性。
  • 写入数据量 > PIPE_BUF:不保证原子性。

3. 命名管道 FIFO

创建方式:

bash 复制代码
mkfifo filename

程序内:

c 复制代码
int mkfifo(const char *filename, mode_t mode);

特点:

  • 命名管道是文件系统中的特殊文件。
  • 可用于无亲缘关系进程间通信。
  • 匿名管道用 pipe 创建并打开;命名管道用 mkfifo 创建,用 open 打开。
  • 创建方式不同,但打开之后语义相同。

打开规则:

  • 读打开:
    • O_NONBLOCK 关闭:阻塞,直到有进程为写而打开 FIFO。
    • O_NONBLOCK 开启:立刻返回成功。
  • 写打开:
    • O_NONBLOCK 关闭:阻塞,直到有进程为读而打开 FIFO。
    • O_NONBLOCK 开启:立刻返回失败,错误码 ENXIO。

典型用途:

  • 文件拷贝。
  • server/client 通信。

三、进程池

目标:

  • 预先创建多个子进程。
  • 父进程通过管道向子进程派发任务。
  • 避免频繁 fork,提高效率。

核心结构:

  • Channel:封装写端 fd、子进程 pid,提供 Send、Close。
  • ProcessPool:负责创建管道、fork 子进程、保存 channel、派发任务、清理。
  • TaskManager:任务函数表,随机选择任务编号。
  • Worker:子进程循环从标准输入读任务编号并执行。
  • Main:初始化进程池、派发任务、清理进程池。

关键流程:

  1. 父进程创建管道。
  2. fork 子进程。
  3. 子进程关闭历史写端,关闭当前管道写端,dup2(pipefd[0], 0),然后执行 Worker。
  4. 父进程关闭读端,保存写端。
  5. 父进程轮询选择 Channel,发送任务编号。
  6. 清理时父进程关闭所有写端,子进程 read 返回 0 后退出,父进程 waitpid 回收。

要点:

  • 子进程只保留读端,父进程只保留写端。
  • 管道用于任务派发。
  • 回调函数 std::function 体现多态思想。
  • 清理时关闭写端是让子进程退出的关键。

四、System V 共享内存

共享内存是最快的 IPC 形式:

  • 共享内存映射到进程地址空间后,进程间数据传递不再经过内核。
  • 进程像访问普通内存一样读写共享内存。

1. 相关函数

shmget
c 复制代码
int shmget(key_t key, size_t size, int shmflg);

作用:

  • 创建或获取共享内存。

参数:

  • key:共享内存名字,通常用 ftok 生成。
  • size:共享内存大小。
  • shmflg:
    • IPC_CREAT:不存在则创建,存在则获取。
    • IPC_CREAT | IPC_EXCL:不存在则创建,存在则报错。
    • 权限标志如 0666。

返回值:

  • 成功返回共享内存标识符 shmid。
  • 失败返回 -1。
shmat
c 复制代码
void *shmat(int shmid, const void *shmaddr, int shmflg);

作用:

  • 将共享内存段连接到当前进程地址空间。

参数:

  • shmid:共享内存标识。
  • shmaddr:
    • NULL:内核自动选择地址。
    • 非 NULL:按规则连接。
  • shmflg:
    • SHM_RND
    • SHM_RDONLY

返回值:

  • 成功返回指向共享内存首地址的指针。
  • 失败返回 -1。
shmdt
c 复制代码
int shmdt(const void *shmaddr);

作用:

  • 将共享内存段与当前进程脱离。

注意:

  • shmdt 不等于删除共享内存。
  • 只是当前进程不再映射它。
shmctl
c 复制代码
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

常用命令:

  • IPC_STAT:获取共享内存状态。
  • IPC_SET:设置共享内存状态。
  • IPC_RMID:删除共享内存段。

2. 使用流程

text 复制代码
ftok 生成 key
  ↓
shmget 创建/获取共享内存
  ↓
shmat 挂接到进程地址空间
  ↓
读写共享内存
  ↓
shmdt 去关联
  ↓
shmctl(IPC_RMID) 删除

3. 重要注意点

  • 共享内存没有同步与互斥。
  • 共享内存缺乏访问控制,会带来并发问题。
  • 需要配合管道、信号量等实现访问顺序。
  • IPC 资源生命周期随内核,必须显式删除,否则不会自动清除,除非重启。
  • 查看共享内存:
bash 复制代码
ipcs -m

删除:

bash 复制代码
ipcrm -m shmid

4. 示例思路

  • server 创建共享内存,并创建 FIFO。
  • client 获取共享内存,写入数据。
  • 通过 FIFO 进行访问控制,让 server 按顺序读取。
  • server 最后 shmdt,再 shmctl(IPC_RMID) 删除共享内存。

五、System V 消息队列(了解)

  • 消息队列提供从一个进程向另一个进程发送数据块的方法。
  • 每个数据块都有类型。
  • 接收者可以按类型接收数据。
  • IPC 资源必须删除,否则不会自动清除。
  • System V IPC 资源生命周期随内核。

六、System V 信号量(了解)

信号量主要用于同步和互斥。

基本概念:

  • 共享资源:多个执行流能看到的同一份公共资源。
  • 临界资源:被保护起来的共享资源。
  • 临界区:访问临界资源的代码。
  • 互斥:任何时刻只允许一个执行流访问资源。
  • 同步:多个执行流访问临界资源时具有顺序性。

信号量理解:

  • 本质是一个计数器。
  • 作用:保护临界区。
  • 本质:对资源的预订机制。
  • P 操作:申请资源,计数器 --。
  • V 操作:释放资源,计数器 ++。

七、内核如何组织管理 IPC 资源

参考 Linux 2.6.18 内核源码。

关键结构:

  • struct ipc_ids
    • in_use
    • max_id
    • seq
    • mutex
    • ipc_id_ary
  • struct ipc_id_ary
    • size
    • struct kern_ipc_perm *p[0]
  • struct kern_ipc_perm
    • lock
    • deleted
    • key
    • uid
    • gid
    • cuid
    • cgid
    • mode
    • seq
    • security

具体资源结构:

  • 消息队列:struct msg_queue
  • 信号量:struct sem_array
  • 共享内存:struct shmid_kernel

理解:

  • 内核用统一的 kern_ipc_perm 管理 IPC 资源的权限和公共属性。
  • 具体资源结构内嵌 kern_ipc_perm,类似面向对象中的"继承"或"多态"。
相关推荐
Ruiery41 分钟前
Linux 6.6内核 CPU 深度解析(八):percpu 变量 — 每个 CPU 一份数据,无锁访问
linux·运维·服务器
91刘仁德1 小时前
传输层协议深度拆解:端口寻址、UDP 报文与 TCP 可靠性机制全解析
linux·网络·c++·网络协议·tcp/ip·udp·c
AIgorithmGEEK1 小时前
[Linux]HTTP 应用层协议全解(中篇)
linux·网络·网络协议·http
玖石书1 小时前
linux 安装uv(python虚拟环境管理)
linux·python·uv
91刘仁德1 小时前
HTTPS 加密原理与 CA 数字证书:从对称加密到完整通信流程
网络·笔记·网络协议·http·https
奈落241 小时前
【RAG 深度修炼】专栏 · 第 9 期(收官):安全专题与生产 Checklist 总集——从能跑的 Demo 到睡得着觉的生产系统
大数据·网络·人工智能·安全·ai编程
皓月盈江1 小时前
Hexo博客Butterfly5.7.0主题网站信息:本站访客数和本站总浏览量的数据一直加载不显示的解决方法
linux·hexo·butterfly·busuanzi·vercount·本站访客数·本站总浏览量
H.莓飛2 小时前
【数据结构】堆_OJ题
linux·数据结构·算法
sogw-三叶草️2 小时前
HITCON CTF 2014 — stkof · Unsafe Unlink
网络·安全·web安全·pwn·堆·二进制安全·堆漏洞