【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,失败返回-1
    if (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 : write
    else if (id == 0)
    {
        //关闭读端
        close(pipefd[0]);

        //写入
        Write(pipefd[1]);

        //写入完成关闭写端
        close(pipefd[1]);
        exit(1);
    }
    // father : read
    close(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空间被占用越来越多,意味着后面的子进程能修改前面的管道文件。

在这里插入图片描述

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


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode—55.跳跃游戏【中等】

2023每日刷题&#xff08;四十&#xff09; Leetcode—55.跳跃游戏 贪心法实现代码 #define MAX(a, b) ((a > b)? (a): (b))bool canJump(int* nums, int numsSize) {int k 0;for(int i 0; i < numsSize; i) {if(i > k) {return false;}k MAX(k, i nums[i]);}r…

DDR-MIG 学习记录

MIG调试总结&#xff1a; 对vivado软件中用于控制DDR2 / DDR3 的 控制器MIG(Memory Interface Generator)IP核进行了仿真测试&#xff0c;以学习如何用IP核来控制FPGA板载SDRAM的读写。我们只需要学会MIG的接口控制就可以。 ①配置IP核 Xilinx 的 DDR 控制器的名称简写为 MIG&…

vue3+vite+ts项目打包时出错

项目中引入了element-plus国家化的配置&#xff0c;然后进行项目打包&#xff0c;报下面的错误 解决方法&#xff1a; 在main.ts中添加 // ts-ignore

【SQL SERVER】定时任务

oracle是定时JOB&#xff0c;sqlserver是创建作业&#xff0c;通过sqlserver代理实现 先看SQL SERVER代理得服务有没有开 选择计算机右键——>管理——>服务与应用程序——>服务——>SQL server 代理 然后把SQL server 代理&#xff08;MSSQLSERVER&#xff09;启…

Vue和React配置解决跨域,proxy代理两步搞定

Vue配置&#xff1a; 第一步&#xff1a; 找到 vite.config.js 文件 进行如下代码配置 import { defineConfig } from "vite"; import vue from "vitejs/plugin-vue"; export default defineConfig({plugins: [vue()],server: {/*** /api 是代理标识*/p…

基于vue框架的美团类药品点单系统

基于VUE框架的美团类药品点单管理系统 摘要&#xff1a; 2019年12月以来&#xff0c;中国湖北省武汉市爆发新型冠状病毒引发的肺炎疫情&#xff0c;并通过人传人的感染方式快速向全国其他地区扩散。全国上下万众一心抗击病毒&#xff0c;湖北广东浙江等24省市启动重大卫生突发…

从四个典型场景看如何将数据集成“用到实处”

一、数据集成概念 数据集成是指将来自不同数据源的数据整合到一个统一的数据存储中&#xff0c;并确保这些数据能够互相关联、交换和共享的过程。在数据集成的过程中&#xff0c;数据通常需要经过清洗、转换和统一格式化等步骤&#xff0c;以确保数据的一致性、完整性和可用性…

RabbitMQ之延迟消息

文章目录 前言一、死信交换机二、延迟消息死信交换机实现延迟消息图解流程 DelayExchange插件实现延迟消息安装插件声明延迟交换机发送延迟消息 总结 前言 死信交换机、延迟消息 一、死信交换机 当一个队列中的消息满足下列情况之一时&#xff0c;可以成为死信&#xff08;dea…

基于springboot实现私人健身与教练预约管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现私人健身与教练预约管理系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应…

编程学习及常见的技术难题

文章目录 编程学习及常见的技术难题引言如何学习编程学习参考开发工具推荐编程中常见的技术难题 编程学习及常见的技术难题 引言 学习编程是一件有趣也有挑战的事情&#xff0c;它可以让你创造出各种有用的软件&#xff0c;解决各种复杂的问题&#xff0c;甚至改变世界。 编程中…

VS2010配置opencv2.4.10

1.下载opencv2.4.10&#xff0c;百度网盘链接如下&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1UdoQJbRUEB_G2urT703xYQ 提取码&#xff1a;7lbd 2.运行opencv-2.4.10.exe&#xff0c;将文件提取到一个自定义目录里&#xff1a; 3.添加系统环境变量 在“系统变量…

序列化基础

1、简介 对象序列化的目标是将对象保存到磁盘中&#xff0c;或允许在网络中直接传输对象。它允许把内存中的 Java 对象转换成平台无关的二进制流&#xff08;序列化&#xff0c;也称编码&#xff09;&#xff0c;并持久地保存在磁盘上或通过网络把这种二进制流传输到另一个网络…

Spring --- 创建一个Spring项目

文章目录 创建一个Maven项目添加Spring框架支持添加启动类 创建一个Maven项目 注&#xff1a;我们需要使用 Maven 来管理依赖&#xff0c;所以需要创建一个Maven项目 添加Spring框架支持 注&#xff1a; 添加这两个依赖才能正确使用 Spring在添加依赖后记得刷新&#xff0c;把依…

Vue3-Pinia

Pinia是什么 Pinia是Vue的最新状态管理工具&#xff0c;是Vuex的替代品 比Vuex更大的优势在于&#xff1a; 1.提供更加简单的API&#xff08;去掉了mutation&#xff09; 2.提供符合&#xff0c;组合式风格的API&#xff08;和Vue3新语法统一&#xff09; 3.去掉了modules…

JOSEF 漏电继电器JHOK-ZBL1 DH-50L 系统1140V 电源AC220V

系列型号&#xff1a; JHOK-ZBL多档切换式漏电&#xff08;剩余&#xff09;继电器 JHOK-ZBL1多档切换式漏电&#xff08;剩余&#xff09;继电器 JHOK-ZBL2多档切换式漏电&#xff08;剩余&#xff09;继电器 JHOK-ZBM多档切换式漏电&#xff08;剩余&#xff09;继电器 …

为品质加冕 | 喜尔康智家再次斩获大奖

近日&#xff0c;被誉为“家居质量界奥斯卡”的2023年度沸腾质量奖颁奖盛典在福建厦门第三届家居质量大会同期隆重举行。现场重磅揭晓2023年沸腾质量奖测评获奖结果。 今年&#xff0c;喜尔康智能家居再接再厉&#xff0c;从数百家参评企业中脱颖而出&#xff0c;参评的智能坐便…

解锁领先的有限元分析软件ABAQUS:不同版本功能特点及价格

随着科学技术的飞速发展&#xff0c;工程领域对于高效可靠的仿真软件需求日益增长。ABAQUS作为有限元分析领域的佼佼者&#xff0c;为工程师提供了强大而灵活的工具&#xff0c;用于模拟和分析复杂的结构和材料行为。本文将深入介绍ABAQUS的概念、不同版本的特点、功能区别、定…

Baby-Step Giant-Step Homomorphic DFT

参考文献&#xff1a; [CT65] Cooley J W, Tukey J W. An algorithm for the machine calculation of complex Fourier series[J]. Mathematics of computation, 1965, 19(90): 297-301.[Shoup95] Shoup V. A new polynomial factorization algorithm and its implementation[…

LeetCode Hot100 84.柱状图中最大的矩形

题目&#xff1a; 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 方法&#xff1a; 代码&#xff1a; class Solution {public int largestRectang…

WIFI HaLow技术引领智能互联,打破通信限制

在过去十年里&#xff0c;WIFI技术已在家庭和企业中建立起了庞大的网络&#xff0c;连接了数十亿智能互联设备&#xff0c;促进了信息的迅速传递。然而&#xff0c;当前的WIFI标准存在一些挑战&#xff0c;包括协议范围的限制和整体功能的受限&#xff0c;导致在较远距离进行通…