Linux进程间通信——管道

Linux进程间通信——管道 一进程间通信的方式在操作系统中进程是资源分配的基本单位每个进程都拥有独立的地址空间相互之间默认无法直接访问数据。为了让多个进程协同工作比如一个进程处理数据、另一个进程展示结果就需要一种“桥梁”来实现数据交换这就是进程间通信IPCInter-Process Communication。通信的核心本质进程具有独立性彼此内存隔离因此通信的本质是1.操作系统需要直接或间接地为通信双方提供公共内存空间 / 资源。2.参与通信的进程必须能共同访问到这份公共资源才能完成数据交换。不同的通信方式如管道、共享内存、消息队列本质区别仅在于这份公共资源是由操作系统的哪个模块提供的而管道Pipe作为最基础、最常用的IPC方式之一广泛应用于命令行交互、简单程序协作等场景。通信的实现步骤要实现进程间通信需要遵循两个核心步骤1.建立共享先让不同进程能看到并访问同一份公共资源。2.数据交互基于这份共享资源完成数据传递、事件通知等通信行为。进程间的通信方式主要有五种分别为管道、信号量、共享内存、消息队列和套接字。通信的目的1.数据传输一个进程需将自身数据发送给另一个进程实现数据的跨进程传递与交互。2.资源共享多个进程之间共享相同的资源如内存、设备、网络等提高资源利用效率。3.通知事件一个进程向另一个或一组进程发送消息告知其发生了特定事件例如进程终止时需通知父进程。4.进程控制部分进程需完全控制另一个进程的执行如 Debug 调试进程需拦截目标进程的所有陷入和异常并及时感知其状态变化。二什么是管道管道是 Unix 系统中历史最悠久的进程间通信机制它本质上是一条单向的数据流通道用于在两个进程之间传递数据 —— 就像一根 “水管”一端由进程写入数据另一端由另一个进程读取数据从而实现进程间的信息交互。1管道核心本质内核级的内存缓存管道并非磁盘上的物理文件而是操作系统内核维护的一块内存缓冲区。它不属于任何文件系统不会占用磁盘或外部存储的空间仅存在于内存中生命周期与关联进程绑定进程结束后管道随之销毁。2通信逻辑单向流转、读写匹配管道遵循先进先出FIFO 的单向通信规则必须通过 “写端 读端” 配合实现一个进程以写模式打开管道对应写端负责向缓冲区写入数据另一个进程以读模式打开管道对应读端负责从缓冲区读取数据数据从写端流入、读端流出本质是将一个进程的输出直接对接至另一进程的输入。3操作特性以文件接口访问内核缓存虽然管道是内存中的特殊结构但系统为其设计了与文件一致的操作接口读写操作可直接使用通用的 read()、write() 系统调用这种 “文件化” 的设计简化了进程间通信的编程逻辑让管道的使用方式与文件操作高度统一。Linux 系统中管道完全基于内存实现本质是以文件操作方式访问的内核内存缓冲区。三管道的分类管道主要分为无名管道和有名管道两类二者的核心差异体现在通信范围上3.1.无名管道别称匿名管道通信限制仅支持亲缘进程间通信亲缘进程指拥有共同祖先的进程例如父子进程、兄弟进程特性无独立标识依赖进程亲缘关系传递数据生命周期随关联进程结束而终止。创建无名管道的命令:pipeint pipe(int pipefd[2]);调用pipe系统调用之后,返回两个文件描述符,fd[0]为读端,fd[1]为写端;实例#include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include assert.h #include string.h int main() { // 1. 创建无名管道 int fd[2]; assert(pipe(fd) ! -1); // fd[0] 读端fd[1] 写端 // 2. 创建子进程 pid_t pid fork(); assert(pid ! -1); // 子进程读端负责从管道读取父进程写入的数据 if (pid 0) { close(fd[1]); // 关闭写端 char buff[128] {0}; // 定义缓冲区存储从管道读取的数据 read(fd[0], buff, 127); // 从读端fd[0]读取数据最多读127字节避免溢出 printf(child read:%s\n, buff); // 打印读取到的数据 close(fd[0]); // 读完数据关闭读端 } // 父进程写端负责向管道写入数据 else { close(fd[0]); // 关闭读端 write(fd[1], hello, 5); // 向写端fd[1]写入数据“hello”长度5字节 close(fd[1]); // 写完数据关闭写端 } exit(0); // 进程正常退出结束程序 }运行得出3.2 有名管道别称命名管道通信范围支持任意两个进程间通信无需进程间存在亲缘关系特性通过文件系统中的名称标识具备独立可见性可被无关进程发现并接入通信。创建有名管道使用命令:mkfifo打开管道:open();关闭管道:close();读数据:read();写入数据:write();实例先创建有名管道编写写入文件write.c#include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include assert.h #include string.h int main() { int fd open(fifo, O_WRONLY);//以只写的方式打开 assert(fd ! -1); printf(fd%d\n, fd); write(fd, hello, 5); close(fd); }编写只读的文件read.c#include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include assert.h int main() { //打开已创建的有名管道 int fd open(./fifo, O_RDONLY);//以只读方式打开 // 断言检查确保管道打开成功 assert(fd ! -1); printf(fd%d\n, fd); //定义缓冲区用于存储从管道读取的数据 char buff[128] {0}; //从管道读端读取数据 read(fd, buff, 127); //打印读取到的数据 printf(read:%s\n, buff); // 关闭管道 close(fd); // 进程正常退出结束程序 exit(0); }在两个终端运行四管道的特点管道类型内核内存空间数据存储文件系统元数据空间磁盘数据空间匿名管道pipe占用64KB 缓冲区无无命名管道FIFO占用64KB 缓冲区少量文件节点无(1)管道必须读,写进程同时open,否则会阻塞;(2)如果管道没有数据,那么read会阻塞;(3)管道的写端关闭,读read返回值为0;(4)管道打开的时候只有只读和只写两种方式,读写方式打开是未定义的.(5)无论有名还是无名,写入管道的数据都在内存中(管道的大小永远为0,面试的重点)(6)管道是一种半双工通信方式(通信方式有单工,半双工,全双工)(7)有名管道和无名管道的区别:有名管道可以在任意进程间使用,无名管道主要在父子进程间通信.(8)管道的读端关闭,写会产生异常