LINUX14 进程间的通信 - 管道

管道

12.1命名管道

12.1.1命名管道(Named Pipe/FIFO)

命名管道克服了普通管道只能在亲缘进程间使用的限制。

特点

  • 在文件系统中有对应的文件名(遵循UNC命名规范)
  • 允许无亲缘关系的进程通信
  • 通过 mkfifo() 系统调用创建
  • 支持多对多通信模式

创建命名管道方法:

复制代码
#include <sys/stat.h>

// 创建命名管道

mkfifo("/tmp/myfifo", 0666);

使用命名管道方法

复制代码
#include<fcntl.h>

int fd=open("/tmp/myfifo",O_WRONLY);//发送数据端

int fd=open("/tmp/myfifo",O_RDONLY);//写入数据端

12.1.2管道读写规则深度理解

  1. 读端关闭:如果所有读端关闭,写端进程会收到SIGPIPE信号,导致进程终止
  1. 写端关闭:读端读取完所有数据后,read返回0,表示文件结束
  1. 原子性保证:当写入数据量不大于PIPE_BUF(通常512B-4KB)时,Linux保证写入的原子性。

示例两个进程进行通信:

接收端a.c文件如下:

cpp 复制代码
int fd=open("fifo",O_RDONLY);

printf("fd=%d\n",fd);

char buff[128]={0};

while(1){

int n=read(fd,buff,127);

if(n==0)break;

printf("buff=%s",buff);

}

发送端b.c文件如下:

cpp 复制代码
void fun(int sig){

printf("sig = %d \n",sig);

signal(SIGPIPE,SIG_DFL);

}

int main(){

signal(SIGPIPE,fun);

mkfifo("fifo",0600);

int fd=open("fifo",O_WRONLY);

printf("fd=%d\n",fd);

char buff[128]={0};

while(1){

fgets(buff,127,stdin);

write(fd,buff,sizeof(buff));

}

return 0;

}

12.1.3管道的工作原理与核心特性

  1. 单向通信:数据只能从写端 ( fd[1] ) 流向读端 ( fd[0] )。要实现双向通信,需要建立两个管道。
  1. 内核缓冲:数据在内核缓冲区中暂存。读进程从缓冲区读取数据,写进程向缓冲区写入数据。
  1. 同步与阻塞
  • 当读进程尝试读取一个空管道 时,它会阻塞,直到有数据写入。
  • 当写进程尝试向一个已满管道 写入数据时,它会阻塞,直到有空间可用。
  • 如果所有读端都已关闭,写进程会收到 SIGPIPE 信号,通常导致进程终止。
  1. 字节流导向:管道不维护消息边界,数据被视为连续的字节流。多次写入可能被一次读取,单次写入也可能被多次读取。

12.2 匿名管道

12.2.1匿名管道(Anonymous Pipe)

匿名管道 (Anonymous Pipe)是Linux/Unix系统中一种最基本的进程间通信机制,主要用于具有亲缘关系的进程间数据传递,匿名管道通过内核缓冲区实现单向数据流,其本质是一个内核维护的环形队列(先进先出)。

关键特性:

  • 单向通信:数据只能从写端流向读端
  • 血缘关系限制:传统匿名管道只能用于父子进程等有亲缘关系的进程间
  • 内核缓冲区:默认大小通常为4KB或64KB,数据存储在内核内存中
  • 同步机制:读空管道会阻塞,写满管道也会阻塞

12.2.2匿名管道工作原理

创建与使用流程

创建管道:

cpp 复制代码
int pipefd[2];//// pipefd[0]读端,pipefd[1]写端

pipe(pipe1); // 父→子

pipe(pipe2); // 子→父

// 1. 创建管道

if (pipe(pipefd) == -1) {

perror("pipe创建失败");

return 1;

}

父子进程通信:

cpp 复制代码
// 2. 创建子进程

pid_t pid = fork();

if (pid == 0) {

// 子进程:关闭写端,读取数据

close(pipefd[1](@ref);

read(pipefd[0], buffer, sizeof(buffer));

printf("子进程收到: %s\n", buffer);

close(pipefd;

} else {

// 父进程:关闭读端,写入数据

close(pipefd;

write(pipefd[1], "Hello from parent!", 18);

close(pipefd[1](@ref);

}

文件描述符继承机制:当父进程调用 fork() 创建子进程时,子进程会继承父进程的文件描述符表,因此父子进程可以通过相同的文件描述符访问同一个管道。

12.2.3匿名管道关键特性

  1. 缓冲区与容量
  • 默认大小:通常为4KB或64KB,可通过系统配置调整
  • 原子操作:写入量小于 PIPE_BUF (通常4KB)时可保证操作的原子性
  1. 阻塞行为

匿名管道的读写操作具有特定的阻塞特性:

情况 读端行为 写端行为
管道空 阻塞等待 正常写入
管道满 正常读取 阻塞等待
写端关闭 读取剩余数据后返回0 -
读端关闭 - 收到SIGPIPE信号
  1. 四种特殊情况的处理

  2. 所有写端关闭:读端读完数据后, read 返回0

  1. 写端未关闭但无数据:读端阻塞等待
  1. 所有读端关闭:写端会收到 SIGPIPE 信号
  1. 读端未关闭但管道满:写端阻塞等待

管道对比

特性维度 匿名管道 (PIPE) 命名管道 (FIFO) 共同点
本质 内核缓冲区 内核缓冲区(但有文件名) 都是内存中的缓冲区,以文件形式抽象
通信方向 半双工,单向流动 半双工,单向流动 单向通信,双向需两个管道
进程关系 必须具有亲缘关系 无需亲缘关系 -
数据模式 字节流,无消息边界 字节流,无消息边界 数据是连续的字节流
同步机制 阻塞I/O,读写空/满管道会阻塞 阻塞I/O,读写空/满管道会阻塞 内核提供同步,保证数据安全
生命周期 随进程创建和销毁 在文件系统中持久存在,直到被显式删除 进程退出会关闭其持有的描述符
创建方式 pipe() 系统调用 mkfifo() 函数或命令 -
可见性 仅限继承文件描述符的进程 文件系统可见,任何进程可访问

选择依据:

  1. 需要在有亲缘关系的进程 (如父子进程)间传递数据时,优先考虑匿名管道
  1. 需要在无亲缘关系的任意进程 间通信时,必须使用命名管道
  1. 对于需要高性能、大数据量共享 的场景,可考虑共享内存;对于需要可靠、结构化消息传递的场景,可考虑消息队列或套接字。
相关推荐
大聪明-PLUS3 小时前
从头开始为 ARM 创建 Ubuntu 映像
linux·嵌入式·arm·smarc
chenzhou__4 小时前
MYSQL学习笔记(个人)(第十五天)
linux·数据库·笔记·学习·mysql
序属秋秋秋5 小时前
《Linux系统编程之入门基础》【Linux基础 理论+命令】(上)
linux·运维·服务器·ubuntu·centos·命令模式
攻城狮7号5 小时前
2025年远程控制软件横评:UU远程、ToDesk、向日葵
网络·开发工具·todesk·向日葵·远程控制软件·uu远程·实测横评
黄沐阳8 小时前
stp,rstp,mstp的区别
服务器·网络·php
一张假钞9 小时前
Ubuntu SSH 免密码登陆
linux·ubuntu·ssh
Wang's Blog10 小时前
Linux小课堂: 文件操作警惕高危删除命令与深入文件链接机制
linux·运维·服务器
水月wwww11 小时前
操作系统——进程管理
linux·操作系统·vim·进程·进程调度
我科绝伦(Huanhuan Zhou)11 小时前
分享一个可以一键制作在线yum源的脚本
linux·运维