2026/8/5 12:12:54

Linux--进程通讯--管道--匿名管道

Linux--进程通讯--管道--匿名管道 1.进程间通信IPC介绍IPC进程通讯的核心、每个进程都有独立的虚拟地址空间互相看不见对方内存里的数据IPC本质就是给两个独立进程搭一条“专属传话通道”让它们能交换数据、互相协调。IPC的目的数据传输A进程把文件、计算结果发给B进程资源共享多个进程共用同一份数据不用各自复制进程同步多个进程干活时互相等待、约束避免抢资源出错事件通知一个进程把运行状态信号发给另一个进程。两个核心维度进程间通讯有两个核心一个是数据传递怎么把数据从A传给B一个是同步控制怎么协调谁先谁后管道、消息队列、共享内存-解决“数据传递”信号量、互斥锁-解决“同步控制”最强大的方案共享内存信号量进程间通讯的发展管道-System V-POSIX当前发展情况和应用情况• 父子进程简单传数据 → 管道就够了• 无亲缘关系传消息 → POSIX 消息队列• 大数据量共享 → POSIX 共享内存 信号量• 维护老代码 → 才用 System V IPC2.管道什么是管道管道就是操作系统在内核里开辟的一块单向缓冲区是进程间最简单的通信通道。规则数据一端写、一端读水流只能单向流动就像家里的水管一头进水、一头出水不能双向同时传输数据专门用来让两个进程互相传递数据。匿名管道匿名管道概念匿名管道pipe没有实体文件名字只存在于内核内存中磁盘上看不见生命周期跟着进程走进程全部结束后管道自动销毁。限制只能用于有血缘关系的进程之间通信父子、兄弟进程。头文件#includeunistd.h功能创建一个无名管道用于进程间通讯函数原型int pipe(int fd[2]);参数说明fd文件描述符数组 fd[0]管道的读端 fd[1]管道的写端返回值成功返回0 失败返回1注意• 管道是半双工的数据只能单向流动。• 匿名管道通常用于具有亲缘关系的进程间通信如父子进程。• 需要先创建管道再创建子进程因为 fork() 会复制文件描述符表子进程才能继承管道。用fork来共享管道的原理1.父进程调用 pipe() 创建管道系统会返回两个文件描述符-fd[0] 管道读端只能读数据-fd[1] 管道写端只能写数据此时父进程同时握着管道的读写两个端口。2.调用 fork() 创建子进程子进程会完整复制父进程的文件描述符表因此父子进程同时拥有管道fd[0]、fd[1]两份句柄父子进程指向内核里同一个管道缓冲区。3.为了保证单向通信父子各自关闭不需要的端口-父进程写数据关闭自己的读端 fd[0] 只用 fd[1] 写入-子进程读数据关闭自己的写端 fd[1] 只用 fd[0] 读取。依靠fork的拷贝机制父子进程就能共用同一个管道完成数据传输。站在文件描述符角度-深入理解管道什么是文件描述符就是内核给文件管道键盘屏幕发的数字编号。0键盘1屏幕2报错屏幕)程序只靠这个数字就能用read或write读写资源。每个进程都有自己专属的编号表互不干扰自己的fd别人不能随便用。怎么理解管道a.文件描述符是进程PCB里文件描述符数组的下标靠这个下标找到内核里真实的file结构体才能操作管道内核缓冲区。用户程序碰不到内核底层。b.调用 pipe(fd[2]) 会一次性生成两个文件描述符 fd[0] 读端、 fd[1] 写端两个fd各自绑定独立的 struct file 但最终指向内核同一块管道缓冲区。c.fork() 创建子进程时会完整拷贝父进程的文件描述符表子进程会拿到一模一样的 fd[0] 和 fd[1] 两个进程的fd指针共同指向同一个内核管道这也是只有亲缘进程才能使用匿名管道的根本原因。d.管道严格单向通信必须手动关闭多余fd写进程关掉读端 fd[0] 读进程关掉写端 fd[1] 。内核依靠 struct file 的引用计数判断管道状态所有写端fd关闭读端read读完数据返回0所有读端fd关闭写端write会触发SIGPIPE信号杀死进程。e.匿名管道无磁盘入口只能靠fork复制fd传递访问权限命名管道FIFO依靠磁盘路径让无关进程打开文件获取fd进而绑定内核管道。站在内核角度-管道本质1.两个file结构体对接文件描述符调用pipe拿到读fd、写fd每个fd在内核绑定一个独立file结构体。读端file只能调用读函数 pipe_read 写功能直接禁用写端file只能调用写函数 pipe_write 读功能直接禁用结构体里的 private_data 相当于一根线把fd和管道核心管控结构连在一起。2.pipe_inode_info管道总控制台管道的总指挥专门管运行状态head写的人下一个要把数据放哪tail读的人下一个从哪拿数据readers/writers统计现在有几个进程拿着读端、写端等待队列:管道空了没东西读读的人就排队睡觉即将读端放入等待队列如果写端写东西进来将读端唤醒继续读取数据。如果管道满了让写端进入等待队列等有读端读走数据腾出位置唤醒写段。引用计数规则必考没有任何进程持有写端writers0所有进程全部关掉写端管道里面剩下的数据读进程可以读完。读完之后直接返回零。表示管道彻底结束没有新数据进来了。没有任何进程持有读端readers0所有进程全部关掉独端内核直接给程序发一个SIGPIPE信号把进程干掉。3.环形缓冲区数据页真正存数据的地方默认64KB物理内存排成环形队列数据从head写入、tail读出指针走到内存末尾自动绕回开头。headtail 不能直接判断空或者满靠长度len区分len0缓冲区空len等于总容量缓冲区满。4.最终总结管道整套结构链路用户fd → file结构体 → pipe_inode_info管控中心 → 环形内存缓冲区对外通过文件描述符操作读写方式和普通文件完全一样所以契合Linux「一切皆文件」的设计理念这就是文件描述符用户视角。而缓冲区、inode管控结构、引用计数、等待队列这些底层实现属于内核视角。