【Linux】匿名管道+进程池


文章目录

  • 前置知识
  • 一、管道的原理
  • 二、管道的特性
  • 三、管道的接口
  • 四、使用管道实现简单的进程池
    • 解决进程池的一个小问题


前置知识

一个进程在创建时,会默认打开三个文件,分别是:stdin,stdout,stderr
进程中有一个维护进程所打开的文件的文件描述对象结构体struct files_struct该文件描述对象结构体中包含一个fd_array,文件描述符表,这个文件描述符表存储的是对应打开的文件的文件描述对象的地址。也就是说,每一个文件都有对应的文件对象,来记录该文件的各种属性struct file。而进程对应的是文件描述对象,两者不同。
在这里插入图片描述
fd_array中存储的就是struct file*类型。

默认打开的三个文件中,stdin,stdout,stderr对应的分别是键盘文件,显示器文件,显示器文件,占用了fd_array文件描述符表中的0,1,2下标。

所以,进程再次创建文件时,会默认从3号下标开始记录。

一、管道的原理

在这里插入图片描述

父进程创建管道文件时,默认打开读端和写端,读端的文件fd存在3号下标中,写端文件存在4号下标中。
子进程被创建时会继承父进程的管理文件的对象,所以子进程的fd_array的3号和4号下标也记录了管道文件的读写端。

为了保证父子进程之间的通信,假设是父进程进行读取,子进程进行写入。
所以需要关闭父进程的写端,关闭子进程的读端。

在这里插入图片描述
子进程进行写入,父进程进行读取,就能实现通信了。

问题:为什么父进程不直接把要发送给子进程的数据保存一份,子进程在创建时就会继承这份数据了。

这种通信方式不是不可以,但只能静态通信。


实际上,在创建管道文件时,会创建两个文件对象,它们存储同一个inode,指向同一块缓冲区,这样就能实现子进程通过写端的struct file和父进程的读端的struct file进而看到同一个文件缓冲区,也就是让不同的进程看到同一份资源。

所以管道通信只能进行单向通信!!!

在这里插入图片描述

二、管道的特性

Linux中,管道的大小一般是4096字节(4KB)

管道的本质就是内存级文件。

  • 1.进程之间使用管道通信,必须具有血缘关系。常用于父子关系。
  • 2.管道通信只能进行单向通信。
  • 3.管道是基于文件的,而文件是随进程的,所以管道的生命周期随进程。
  • 4.这个管道文件,没有路径,没有名字,更没有inode,因为使用该管道文件,是由操作系统创建并管理的,而父子进程之间通过该管道进行通信的原因是继承,所以该管道就叫做匿名管道。
  • 5.父子进程是会进行进程协同,同步与互斥的。我的理解是:父子进程要向管道文件中读写内容,就要调用write和read系统调用,而该函数会进行阻塞地等待或读取。
    • 由此可知,管道的读写中有4种情况:
    • 1.读写端正常,如果管道为空,读端就要阻塞。
    • 2.读写端正常,如果管道被写满了,写端就要阻塞。
    • 3.读端正常读,写端关闭,读端就会读到0,表明读到了文件结尾,不会被阻塞。
    • 4.写端正常写,读端关闭,写端不会再写了,没有意义了,因为没人读。

操作系统所做的这一切,本质就是让不同的进程看到同一份资源。

三、管道的接口

在这里插入图片描述
该系统接口的参数是一个数组,数组有两个元素,记录的就是打开的管道文件的读端和写端在fd_array中的位置。

所以我们只需要传一个数组过去即可。

如果成功返回0,失败返回-1,且错误码被设置。

所以该参数叫做输出型参数

因为会把用户传进来的参数进行设置修改,所以用户可以再次使用该参数。

使用方法:

#define SIZE 2
int pipefd[SIZE] = {0};
int n = pipe(pipefd);

这是父进程申请管道文件,父进程需要读取,所以关闭写端

clode(pipefd[1]);

附带的一个函数:
在这里插入图片描述
printf函数我们熟悉,向显示器中打印格式化内容。
snprintf函数是printf函数的变形,本应该向显示器文件中打印的内容,变成向str指针指向的文件中打印size大小的格式化内容。

snprintf(buffer,sizeof(buffer),"%s-%d-%d",s.c_str(),self,cnt);

匿名管道的测试代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cerrno>#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define SIZE 2
#define NUM 1024
using namespace std;// 1.先创建管道文件
// 2.创建子进程
// 3.子进程进行写入,父进程进行读取//向指定文件描述符对应文件写入
void Write(int wfd)
{string s = "Hello , i am child";char buffer[NUM];//getline(cin,buffer);pid_t self = getpid();int cnt = 5;while(cnt--){buffer[0] = 0; // 告诉读者我的buffer当作字符串来用snprintf(buffer,sizeof(buffer),"%s-%d-%d",s.c_str(),self,cnt);  cout << buffer << endl;write(wfd,buffer,strlen(buffer));sleep(1);}}void Read(int rfd)
{char buffer[NUM];while(true){buffer[0] = 0;ssize_t n = read(rfd,buffer,sizeof(buffer));//n是读取到的个数if(n > 0) {buffer[n] = '\0';cout << "father-" << getpid() <<  "get a message from child:[" << buffer << "]#" << endl;}else if(n == 0){cout << "father read file done!" << endl;break;}else break;sleep(1);}
}int main()
{int pipefd[SIZE] = {0};int n = pipe(pipefd);//成功返回0,失败返回-1if (n < 0) // 管道创建失败{perror("pipefd fail");return 1;}// 管道创建成功cout << "pipefd[0] : " << pipefd[0] <<  " pipefd[1] : " << pipefd[1] << endl; //创建子进程pid_t id = fork();if (id < 0){perror("fork fail");return 2;}// child : writeelse if (id == 0){//关闭读端close(pipefd[0]);//写入Write(pipefd[1]);//写入完成关闭写端close(pipefd[1]);exit(1);}// father : readclose(pipefd[1]);Read(pipefd[0]);int status = 0;pid_t rid = waitpid(id,&status,0); // 阻塞等待if(rid < 0)return 3;else if(rid > 0)cout << "wait child process success!" << endl;close(pipefd[0]);return 0;
}

四、使用管道实现简单的进程池

进程池:一个父进程通过创建多个子进程,然后将不同的任务派发给不同的进程,从而提高工作效率。

相比于接到一个任务后,再创建子进程,然后再将该任务交给子进程去做。

进程池的方法是一次创建多个子进程来待命,只要有任务,就可以立即派发,多个任务也能实现并行。

在这里插入图片描述

而父进程与子进程实现通信的方式就是管道通信

进程池代码

解决进程池的一个小问题

在父进程创建子进程时,子进程会继承父进程的struct files_struct,所以在创建第二个子进程时,由于它继承了父进程的信息,导致第二个子进程有能力去修改父进程与第一个子进程进行通信的管道文件。

所以在父进程不断创建子进程的过程中,子进程的fd_array空间被占用越来越多,意味着后面的子进程能修改前面的管道文件。

在这里插入图片描述

解决办法,在父进程创建第二个子进程开始,把该子进程中指向第一个管道文件的写端全部关闭。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/217129.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境爬虫模型训练实际应用 模块实现1. 数据准备1&#xff09;爬虫下载原始图片2&#xff09;手动筛选图片 2. 数据处理1&#xff09;切割得到人物脸部2&#xff09;重新命名处理后的图片3&#xff09;添加到数据集 3. 模型训练及…

深入解析Selenium动作链:精通点击、拖拽、切换等操作

背景&#xff1a; 一些交互动作都是针对某个节点执行的。比如&#xff0c;对于输入框&#xff0c;我们就调用它的输入文字和清空文字方法&#xff1b;对于按钮&#xff0c;就调用它的点击方法。其实&#xff0c;还有另外一些操作&#xff0c;它们没有特定的执行对象&#xff0…

JAVA创建线程方式有几种

方式1&#xff1a;继承Thread类 步骤&#xff1a; 创建一个继承于Thread类的子类重写Thread的run()方法创建当前Thread子类的对象通过实例对象调用start()方法&#xff0c;启动线程----》JAVA虚拟机会调用run()方法 实现&#xff1a; public class TestMyThread {public sta…

html实现各种瀑布流(附源码)

文章目录 1.设计来源1.1 动态响应瀑布流1.2 分页瀑布流1.3 响应瀑布流 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134613121 html实现各种瀑布流(附源码)&#xff0c;…

pwn:[NISACTF 2022]ReorPwn?

题目 按正常方式走&#xff0c;发现指令被反着输出

十大排序之计数排序、桶排序、基数排序(详解)

文章目录 &#x1f412;个人主页&#x1f3c5;算法思维框架&#x1f4d6;前言&#xff1a; &#x1f380;计数排序 时间复杂度O(nk)&#x1f387;1. 算法步骤思想&#x1f387;2.动画实现&#x1f387; 3.代码实现 &#x1f380;桶排序&#x1f387;1. 算法步骤思想&#x1f38…

list的总结

目录 1.什么是list 1.1list 的优势和劣势 优势&#xff1a; 劣势&#xff1a; 2.构造函数 2.1 default (1) 2.2 fill (2) 2.3 range (3) 2.4 copy (4) 3.list iterator的使用 3.1. begin() 3.2. end() 3.3迭代器遍历 4. list容量函数 4.1. empty() 4.2. siz…

开源vs闭源,处在大模型洪流中,向何处去?

文章目录 一、开源和闭源的优劣势比较1.1 开源优势1.2 闭源的优势 二、开源和闭源对大模型技术发展的影响2.1 数据共享2.2 算法创新2.3 业务拓展2.4 安全性和隐私2.5 社会责任和伦理 三、开源与闭源的商业模式比较3.1 盈利模式3.2 市场竞争3.3 用户生态3.4 创新速度 四&#xf…

基于厨师算法优化概率神经网络PNN的分类预测 - 附代码

基于厨师算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于厨师算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于厨师优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

Java Web 学习之路(1) —— 前端篇

文章目录 前言1. JS1.1 引入方式1.2 基础语法1.3 函数1.4 对象1.5 事件监听 2. Vue3. Ajax4. Element5. Nginx 前言 在学习后端前&#xff0c;还需要大致了解下前端的一些知识&#xff0c;所以本篇就先快速把前端的一些知识过一遍。本篇不含过多干货和技术知识&#xff0c;仅仅…

python排序算法_归并排序

什么是归并排序&#xff1a; 归并排序是一种基于分治法的排序算法。它的基本思想是将待排序的序列分成若干个子序列&#xff0c;分别进行排序&#xff0c;然后再将已排序的子序列合并成一个有序的序列。 基本思想&#xff1a; 归并排序是用分治思想&#xff0c;分治模式在每一…

Loadrunner安装大全

目录 一 、下载篇 二、安装篇 三、破解篇 四、Loadrunner支持哪些操作系统&#xff1f; 五、安装Loadrunner需要满足哪些系统要求&#xff1f; 六、安装Loadrunner时是否需要注意什么问题&#xff1f; 七、安装完成后如何验证Loadrunner是否正常工作&#xff1f; 八、如…