当前位置: 首页 > news >正文

Linux系统编程---进程间管道通信

1、进程间通信

        在前面几篇笔记博客文章中,主要是对Linux系统编程中多进程常间的基础知识梳理。从这篇文章开始,将对Linux系统编程的进程间通信知识进行梳理,理清知识脉络。首先会讲解Linux的管道通信(pipe),就是本篇讲解的匿名管道和有名管道通信,接着是信号(signal)部分,然后信号量与PV操作,消息队列(queue),共享内存等。

        起初是想把Linux系统编程的内容全部整合到一篇笔记文章中,但是考虑到内容比较多,加上在后面学到的东西越来越多时,容易遗忘,当需要翻阅笔记时,不仅不方便关注的网友参考学习,也不方便自己去复习。因此分篇梳理,在后面一看文章题目,就能确定是否是自己需要的内容。

        有需要的网友,可以查阅我的系统编程专栏参考交流:

系统编程_奔跑的蜗牛!的博客-CSDN博客https://blog.csdn.net/weixin_49337111/category_12952742.html?spm=1001.2014.3001.5482

        什么是进程间通信,有什么作用?

        在Linux系统中,进程间通信(Inter-Process Communication, IPC)是不同进程之间进行数据交换和同步的过程。在Linux中提供了多种机制来实现进程间的通信:管道(Pipe)、信号(Signals)、消息队列(Message Queues)、共享内存(Shared Memory)、信号量(Semaphores)、套接字(Sockets)。

        为了更加直观了理解进程间通信,举个简单的例子:

./execute     -> 开启了一个名字叫execute的进程。
./control     -> 开启了一个名字叫control的进程。
通过进程之间的通信,使得不同的进程之间能够实现数据的交换,上面的control进程发送数据给execute进程,execute进程收到数据之后,就可以根据数据做出相应的事情。(control进程控制execute进程)

        通过上面对IPC介绍,或多或少都对进程间通信有一定的了解了,不至于听到就头大,一头雾水,接下来部分开始介绍这篇博客文章的重点内容,管道通信

2、匿名管道(PIPE)

2.1、什么是匿名管道

        匿名管道,又称为无名管道,它只能作用于具有亲缘关系的进程之间的通信,例如父子进程间。无名管道就是一个没有名字的管道文件,相当于一个队列结构,fd[1]为写入端(入队),fd[0]为读出端(出队)。无名管道中的信息读出后即删除,再次读取时即为下一个信息。

        不管是匿名管道还是有名管道,在Linux系统下都属于文件的范畴,区别是匿名管道没有名称,因此无法使用open创建或打开,事实上匿名管道有自己独特的创建接口(函数),但其读写方式与普通的文件一样,支持read()/write()操作。

        管道文件事实上还包括网络编程中的核心概念套接字,所谓的管道指的是这些文件不能进行“定位”,只能顺序对其读写数据,就像一根水管,拧开水龙头不断读取,就可以源源不断读到水管中的数据,但如果没有水出来那只能继续等待,不能试图“跳过”部分文件去读写水管的中间地带,这是管道的最基本的特性。

        在Linux命令行中,管道使用竖线 | 连接多个命令,这被称为管道符,通过管道符创建的匿名管道

$ command1 | command2

        上面这条命令的功能是将前一个命令(command1)的输出,作为后一个命令(command2)的输入。

2.2、匿名管道API

//函数原型:
#include <unistd.h>int pipe(int pipefd[2]);    //执行这个函数之后,得到两个文件描述符 --> int  *pipefd//函数功能:
创建一个无名管道文件//参数说明:
pipefd:一个具有2个int类型变量的数组。//返回值:
成功:0
失败:-1

        说明:

1、pipefd[0] -> 读端 pipefd[1] -> 写端

2、没有名字,因此无法使用 open( )。

3、只能用于亲缘进程间(比如父子进程、兄弟进程、祖孙进程……)通信,因为他只能在一个进程中被创建出来,然后通过继承的方式将他的文件描述符传递给子进程。

4、半双工工作方式:读写端分开。

        总结一句话,匿名管道适用于一对一的、具有亲缘关系的进程间的通信。

2.3、匿名管道程序示例

        实现使用无名管道,实现父子进程间管道通信。

#include<stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char **argv)
{int fd[2];//创建一个无名管道文件 ,创建成功之后 fd[0] 读端  fd[1] 写端pipe(fd); //注意,不可以使用open打开无名管道,因为它没有名字,创建的时候默认已经打开了printf("fd[0]:%d fd[1]:%d\n",fd[0],fd[1]);//创建一个子进程 ,实现父进程与子进程的通信pid_t id = fork();if(id == -1)//出错{printf("fork error\n");return -1;}else if(id >0)//父进程{printf("[%d]我是父进程...\n", getpid());sleep(1);//给子进程发送数据,也就是说将数据写入 fd[1] 管道中printf("[%d]父进程,给子进程发送数据:",getpid());           char sendbuf[1024] ={0};scanf("%s",sendbuf);write(fd[1],sendbuf,strlen(sendbuf));}else if(id == 0)//子进程{printf("[%d]我是子进程...\n", getpid());sleep(1);//接收父进程的信息,也就是说从 读端 管道中 读取数据 fd[0]char recvbuf[1024]={0};read(fd[0],recvbuf,sizeof(recvbuf));        printf("[%d]子进程,接收父进程数据:%s\n",getpid(),recvbuf);            }int status; waitpid(-1,&status,WCONTINUED);//阻塞等待子进程的退出return 0;
}

3、有名管道(FIFO)

3.1、 什么是有名管道

        有名管道文件就是一个有名字的管道文件。在linux下,所有的进程都是可以看到这个文件,所以有名管道作用范围是整个linux系统下任意的两个进程

        有名管道是跟匿名管道相对而言的,从外在形态上来看,具名管道更接近普通文件,有文件名、可以open打开、支持read()/write()等读写操作。

        有名管道通常又被称为FIFO(First In First Out),这其实所有管道的基本特性,那就是放入的数据都是按顺序被读出,即所谓先进先出的逻辑。

        当然,管道并不是普通文件,有名管道主要有如下特性:

        (1)与PIPE一样不支持定位操作lseek( )

        (2)与PIPE一样秉持相同的管道读写特性

        (3)使用专门的接口来创建:mkfifo( )(匿名管道是pipe( )

        (4)在文件系统中有对应节点,支持使用 open( ) 打开管道(匿名管道不具备

        (5)支持多路同时写入(匿名管道不具备

3.2、有名管道API

//函数原型
#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);//函数功能:
创建一个有名管道文件。//参数说明:
pathname --> 需要创建的管道文件的路径名(不建议在linux虚拟机的共享路径下创建)
mode     --> 创建管道文件的初始权限,如0777//返回值:
成功 返回0 
失败 返回 -1 ,并设置错误码        

        说明:

        (1)pathname是有名管道的名称,如果要新建的管道文件,需要保证创建的路径位于Linux系统内,如果在虚拟机中操作的时候,不可将管道文件创建在共享文件夹中,因为共享文件夹是windows系统,并不支持管道文件。建议创建在 /tmp 下, 因为管道文件是一个系统级别的资源文件,不会因为进程退出而消失,需要手动删除,因此在TMP下系统重启后会自动清除

        (2)mode是文件权限模式,例如0666,注意权限须为八进制,且实际管道的权限还受系统 umask 的影响。

3.3、有名管道程序示例

        创建有名管道文件,并实现两个进程的通信。

(1)、进程1:写入管道数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>//注意 有名管道文件 不能在共享文件下 创建
#define  FIFO_FILE   "/home/Snail/Desktop/process/fifo"// ./fifo_w  -----> 发送数据     "hello"  ----> 管道文件int main(int argc, char*argv[])
{//判断管道文件是否存在,如果存在,则不再创建if(access(FIFO_FILE, F_OK) == -1)     //文件不存在则返回 -1{//1、先创建一个有名管道文件,注意 仅仅是创建 ,mkfifo没有 打开功能int ret = mkfifo(FIFO_FILE, 0777);if(ret == -1){perror("mkfifo error");return -1;}}//2、打开有名管道文件int fd = open(FIFO_FILE, O_RDWR);if(fd == -1){perror("open fifo error");return -1;}while(1){printf("请输入你要发送的数据:");char sendbuf[1024] = {0};scanf("%s",sendbuf);//3、往 有名管道文件 写入数据  write(fd, sendbuf, strlen(sendbuf));}//4、关闭文件close(fd);return 0;
}

(2)、进程2:读取管道数据

#include<stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>//注意 有名管道文件 不能在共享文件下 创建
#define  FIFO_FILE   "/home/Snail/Desktop/process/fifo"//./fifo_r  读取数据  从 管道文件中 int main(int argc, char*argv[])
{//先判断管道文件是否存在,如果存在,则不需要再创建了if(access(FIFO_FILE, F_OK) == -1) //文件不存在则返回 -1{//1、先创建一个有名管道文件,注意 仅仅是创建 ,mkfifo没有 打开功能int ret = mkfifo(FIFO_FILE,0777);if(ret == -1){perror("mkfifo error");return -1;}}//2、打开有名管道文件int fd = open(FIFO_FILE,O_RDWR);if(fd == -1){perror("open fifo error");return -1;}//3、从管道文件中读取数据//如果没有数据,则会阻塞 等待 管道文件中有数据,才会读取while(1){char recvbuf[1024]= {0};read(fd,recvbuf,sizeof(recvbuf));printf("recvbuf:%s\n",recvbuf);}//4、关闭文件close(fd);return 0;
}

        在目标路径下,可以找到创建的有名管道文件。

4、总结

        ①、管道是创建在内存中,进程结束空间释放,管道不复存在。

        ②、无名管道和有名管道都是半双工通信,实现双向通信需要建立两个管道。

        ③、无名管道是linux特殊文件。

        ④、无名管道只用于父子进程之间有名管道可用于任意两个进程之间。

相关文章:

  • 通讯协议开发实战:从零到一打造企业级通信解决方案
  • Spring AI版本1.0.0-M6和M8效果比较
  • SAM-Decoding_ 后缀自动机助力大模型推理加速!
  • JSON Web Token 默认密钥 身份验证安全性分析 dubbo-admin JWT硬编码身份验证绕过
  • 【2025软考高级架构师】——2024年05月份真题与解析
  • 数据采集文氏管旋风高效湿式除尘器文丘里旋风除尘组合实验装置
  • MFiX(Multiphase Flow with Interphase eXchanges)软件介绍
  • 从 AWS Marketplace 开始使用 AssemblyAI 的语音转文本模型构建语音智能
  • 架构思维:使用懒加载架构实现高性能读服务
  • 工业认知智能:从数据分析到知识创造
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】2.2 多表关联技术(INNER JOIN/LEFT JOIN/FULL JOIN)
  • 单细胞测序数据分析试验设计赏析(二)
  • TFQMR和BiCGStab方法比较
  • 如何在 PowerEdge 服务器上设置 NIC 分组
  • AI 编程日报 · 2025 年 5 月 04 日|GitHub Copilot Agent 模式发布,Ultralytics 优化训练效率
  • 【C++】哈希表
  • strstr()和strpbrk()函数的区别
  • 自闭症谱系障碍儿童的灰质与白质之间的异常功能协方差连接
  • function包装器的意义
  • 解决 Builroot 系统编译 perl 编译报错问题
  • 长三角铁路今日预计发送386万人次,沪宁、沪杭等方向尚有余票
  • 今天全国铁路、公路进入返程高峰,这些路段时段通行压力大
  • 当AI开始谋财害命:从骗钱到卖假药,人类该如何防范?
  • 老人误操作免密支付买几百只鸡崽,经济日报:支付要便捷也要安全
  • 河南博物院:警惕非官方网络平台 “买讲解保进馆” 等虚假信息
  • 力保夏粮丰收,粮食大省江苏多地党政主官到田间察看小麦长势