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

相关推荐
ynchyong1 小时前
Linux nohup 后台服务合并标准错误输出到一个文件
java·linux·运维
xx~t1 小时前
嵌入式——51单片机3
linux·c语言·嵌入式硬件·51单片机
Tairitsu_H1 小时前
[C++] C++11 Lambda与包装器深度解析
开发语言·c++·c++11·lambda·包装器
傲世仙尊1 小时前
从磁盘硬件到Ext文件系统-Linux磁盘级文件系统学习笔记
linux·运维·服务器·开发语言·c++
星源~1 小时前
Linux - Trae Code Linux 环境嵌入式开发指南
linux·mcu·嵌入式·trae code
曦夜日长1 小时前
Linux系统篇,进程概念(三):进程状态详解、僵尸进程与孤儿进程的深度解析、Slab机制的了解
linux·运维·服务器
liulilittle2 小时前
为什么采用全局管理缓存及状态:麻将客户端状态管理
服务器·网络·游戏·客户端·异步·mahjong·麻将
FlightYe2 小时前
音视频修炼之编码器(一):AVC、HEVC编码器内部
android·linux·c++·音视频
云栖梦泽2 小时前
网络设备驱动(1)
linux·arm开发·嵌入式硬件