Linux 进程间通信(IPC)机制详解:管道、FIFO 与 System V 共享内存

Linux 进程间通信(IPC)

所有进程间通信的前提是:先让需要进行通信的进程看到同一份资源(内存)

匿名管道(通常用于父子通信)

  1. 可使用pipe()系统调用函数创建匿名管道,函数原型为int pipe(int fd[2]),函数功能为创建一个匿名管道文件,并分别使用读、写两种方式打开,并将参数fd[0]指向读打开的文件描述符,fd[1]指向写打开的文件描述符,若创建失败则返回-1,成功则返回0

  2. 在创建子进程之前打开一个文件且未关闭时,创建子进程后子进程会对父进程的文件描述符表进行深拷贝,确保文件描述符指向的文件对象中维护的引用计数++,因此,若想不依赖RAII以及进程结束自动释放struct_file,就需要在父子进程中各进行一次close操作(如下图,创建子进程会复制父进程PCB,深拷贝文件描述符表,且其实图中的fiules_struct本身也是内存级的,创建子进程时也会拷贝一份)

  3. 建立匿名管道流程图示

    注:图中的第三步,关闭父子进程中各自不需要的文件描述符是必要步骤,原因是,若都不关闭,则会导致子进程写入数据完毕,退出,父进程read函数会读取管道文件中的数据,当读取完毕时,发现管道的写操作引用计数不为1(因为父进程的写没关),则会直接阻塞(注:一般文件由于本身大小确定,就算同时被读写方式打开,当读取结束,即读到文件结束符时,read函数会直接返回0,而非阻塞,但管道文件是特例)

  4. 匿名管道是可以允许多个进程共同使用的,但在实际应用中,为避免数据混乱,一般仅用来进行一对一通信

匿名管道代码示例:

https://gitee.com/xiao-dongs-code-repository/gitee_dmk/tree/master/Linux/2026_2_27

匿名管道的5种特性及4种通信情况:

5种特性:

  1. 匿名管道,只能用来进行具有血缘关系的进程进行进程间通信
  2. 管道文件,自带同步机制(管道的读写操作具有阻塞特性,因此内核能够在读写双方之间提供基本的同步机制)
  3. 管道是面向字节流的
  4. 所有的管道都是单向通信的,也就是半双工中的一种特殊情况(注,若创建匿名管道时不进行第三步(即上面提到的关闭父子中不需要的文件描述符),这样的管道也不能称为双向通信,因为双向通信有"数据相互独立,不会造成混乱"的隐含条件
  5. 所有文件(包括管道文件)的fd的生命周期,是随进程的(即若不使用close,dup2等函数主动关闭进程中打开的文件,则该文件会一直存在至该进程结束,进程结束时则会自动关闭)

4种情况:

  1. 写慢读快:类似创建匿名管道时注释说明的情况,读端被阻塞,直到写端关闭或继续写入数据

  2. 写快读慢:当管道文件被写满时,为防止再次写入覆盖上面未被读取的数据,会将写端阻塞

  3. 写关读继续:会将管道中的全部数据读完,然后检测到写端的引用计数为0,则read函数会直接返回0(类似普通文件读取到文件结束符的行为)

  4. 读关写继续:很显然,这是一种无用功,就算再怎么写,也不会被其他进程得知,而由于操作系统会检测无用行为,因此会直接使用13(SIGPIPE)信号杀掉写端进程,不过值得一提的是,这个信号并不是在读端关闭后直接发送信号(即不是close()后就立刻杀死对应读端进程),而是当读端被关闭,写端再一次尝试写入后,发现对应读端被关闭,才会发送13信号(可将写端使用getline函数手动输入数据测试出这个性质)

4种情况的测试代码:

https://gitee.com/xiao-dongs-code-repository/gitee_dmk/blob/master/Linux/2026_3_2/anonymous_pipe_test.cpp

命名管道

  1. 创建命名管道:可使用位于sys/stat.h库的mkfifo(const char *pathname, mode_t mode)系统调用,函数功能为创建一个命名管道,参数pathname为管道文件名,mode为管道文件的权限(常使用八进制数0666,但需注意系统的权限掩码umask),返回值为0表示成功,返回-1表示失败
  2. 与匿名管道不同,命名管道与普通文件类似,会在指定目录下创建一个文件类型为p的命名管道文件,且匿名管道仅能实现具备血缘关系的进程(如父子进程中需要子进程继承父进程文件描述符表来实现通信),但命名管道由于其原理与一般文件极其相似,可允许任意进程之间进行通信
  3. 使用命名管道进行通信的方式很简单,首先使用mkfifo函数创建一个命名管道文件,然后想通信的进程间仅需要对该命名管道使用常规文件操作即可,如一个进程server使用read方式打开命名管道,另一个进程client使用write方式打开同一个命名管道,接下来,server与client之间就可以通过这个命名管道fifo实现通信了
  4. 命名管道也支持多个进程相互通信
  5. 可使用unlink系统调用函数删除文件(本质是删除硬连接,当删除后引用计数为0时会删除文件,可用于删除普通文件、命名管道等除目录外的文件)

命名管道的4种特性及4种通信情况

命名管道其实与匿名管道的5种特性及4种通信情况一样,只不过没有匿名管道只能血缘通信的限制

命名管道代码示例:

https://gitee.com/xiao-dongs-code-repository/gitee_dmk/blob/master/Linux/2026_3_7/mkfifo_server.cpp

命名管道的RAII封装示例:

https://gitee.com/xiao-dongs-code-repository/gitee_dmk/tree/master/Linux/2026_3_8

systemV共享内存

共享内存的特性

  1. 共享内存会挂载到内存中的共享区
  2. 共享内存不会随着进程的结束而自动销毁,其生命周期是跟随操作系统的
  3. 共享内存具备shmid编号,从1开始递增,就算删除的前面分配的共享内存,再次创建也不会重复使用前面的编号(除非编号到达极限,则会巡回使用)
  4. 建议使用shmid控制共享内存,而非创建共享内存时使用的key
  5. 共享内存的使用方式类似malloc函数申请的堆区空间,只不过shmget申请的空间会被挂载至共享区
  6. systemV共享内存本身为通信速度舍弃了信息的准确传递(不具备信息的保护性),在现代工程种很少使用,但老项目中可能常见

共享内存的创建

  1. 创建:使用shmget(key_t key, size_t size, int shmflg)系统调用,参数key为命名键值,size为申请内存的字节数,shmflg为共享内存的权限,若创建成功,则返回值为共享内存在系统中的编号(shmid),失败则返回-1,其中key一般需要通过ftok(const char *pathname, int proj_id)函数获取,第一个参数建议传一般文件路径 + 名称,而非目录文件(当然目录文件也可以生成key),不过切记,不能使用不存在的文件进行创建,第二个参数没有要求(ftok函数本质是一个算法,其依赖pathname中的inode编号,因此,若传递相同的pathname与proj_id,则返回的key相同)
  2. 挂载:使用shmat(int shmid, const void *shmaddr, int shmflg)系统调用,参数shmid为共享内存的ID,shmaddr为共享内存的起始地址,shmflg为共享内存属性,若成功则返回值为共享内存的映射到调用的进程的起始虚拟地址,挂载失败则返回-1
  3. 卸载:使用shmdt(const void *shmaddr)系统调用,参数shmaddr为共享内存映射到调用进程的起始虚拟地址,返回值为0表示成功,返回-1表示失败
  4. 删除:使用shmctl(int shmid, int cmd, struct shmid_ds *buf)系统调用,其具备多种功能,但需要删除时,参数shmid为信号量的ID,cmd应填入IPC_RMID,,buf处传入nullptr即可,返回值为0表示成功,返回-1表示失败,需要注意的是,该函数的更准确功能是,将共享内存标记为延迟删除,当没有任何一个进程挂载该共享内存时,该内存才会被删除

创建共享内存代码示例:

https://gitee.com/xiao-dongs-code-repository/gitee_dmk/tree/master/Linux/2026_3_10

相关推荐
如意猴4 小时前
【C++】007 C/C++ 内存管理机制、malloc与new的区别及模板初阶
java·c++·算法
树下水月4 小时前
Typora破解
linux·服务器·前端
朝朝辞暮i4 小时前
C++ 第 32 课:main()、rclcpp::init()、spin()、shutdown()
开发语言·c++·算法
汉克老师4 小时前
GESP2026年9月认证C++一级( 第三部分编程题(1、新龟兔赛跑))精讲
c++·gesp·小学生·学c++编程
沫璃染墨5 小时前
《从零入门Linux系统篇(五十七):线程篇·十——生产者消费者模型进阶:从环形缓冲区到POSIX信号量》
linux·运维·服务器·开发语言·c++·系统架构·信号处理
wuminyu5 小时前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
深念Y5 小时前
rime-雾凇拼音-配置记录
linux·junit·软件·拼音·kde
知识分享小能手5 小时前
C++ 学习教程,从入门到精通,string类和标准模板库 — 完整知识点详解(16)
开发语言·c++·学习
无忧.芙桃5 小时前
位图妙用:512MB 实现 40 亿整数的存在性查询
c语言·c++·面试·哈希算法
星夜夏空996 小时前
网络编程(5)—— Reactor实现(v3)
服务器·网络·php