【Linux】匿名管道+进程池

news2024/11/17 21:25:06

文章目录

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


前置知识

一个进程在创建时,会默认打开三个文件,分别是: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空间被占用越来越多,意味着后面的子进程能修改前面的管道文件。

在这里插入图片描述

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


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

社区物联网云服务架构设计

文章目录 1 摘要2 架构图2.1 社区物联网云服务网络拓扑图2.2 社区物联网云服务通讯流程图2.3 社区远程开锁功能流程图 3 应用场景 1 摘要 随着社区管理越来越智能化&#xff0c;社区物联网升级与改造的市场空间也越来越大。社区物联网包含楼宇对讲、门禁门锁、通道闸等等设备系…

浅谈国内智能制造现状和未来发展趋势

进人二十一世纪&#xff0c;互联网、新能源、大数据等技术的迅猛发展&#xff0c;从而使得社会发生巨大的改变&#xff0c;人类生产工业发生变革。为应对全球挑战&#xff0c;我国根据发展的实际情况&#xff0c;提出《中国制造2025》的国家战略规划。毋庸置疑的是&#xff0c;…

大数据题目的解题技巧

目录 大数据题目的技巧总括 实例精析 实例一 实例二 实例三 大数据题目的技巧总括 &#xff08;1&#xff09;哈希函数可以把数据按照种类均匀分流&#xff1b; &#xff08;2&#xff09;布隆过滤器用于集合的建立与查询&#xff0c;并可以节省大量空间&#xff1b; &…

Git永久或者限时保存用户名及密码,解决每次拉取或者提交代码时都需要手动输入验证信息

介绍 这里以我自身项目情况为例: 依据项目要求,这边使用了 TortoiseGit进行项目的统一管理,下载了 TortoiseGit克隆项目之后,每次拉取或者提交代码,都会弹出一个提示框,要求输入用户名及密码。 解决方式 单个仓库内设置,只作用于对当前仓库 在当前项目目录文件夹下,…

vue3之echarts渐变柱状图

vue3之echarts渐变柱状图 效果&#xff1a; 核心代码&#xff1a; <template><div class"abnormal"><div class"chart" ref"chartsRef"></div></div> </template><script setup> import * as echa…

【EI会议征稿】第三届电子信息技术国际学术会议(EIT 2024)

The 3rd International Conference on Electronic Information Technology 第三届电子信息技术国际学术会议&#xff08;EIT 2024&#xff09; 电子信息工程在我国信息化产业的发展过程中举足轻重&#xff0c;且随着现代社会的发展&#xff0c;航空航天领域、制造业领域和智能…

2024年度投资策略:AI大模型和半导体国产化加速

今天分享的是AI系列深度研究报告&#xff1a;《2024年度投资策略&#xff1a;AI大模型和半导体国产化加速》。 &#xff08;报告出品方&#xff1a;东方证券&#xff09; 报告共计&#xff1a;48页 前言: 行情回顾与未来展望 电子板块涨幅转正&#xff0c;信心逐渐回归。截至…

7000家门店的盈利增长,从导购的人效提升开始

管理是艺术&#xff0c;还是科学&#xff1f; 说管理是艺术&#xff0c;是因为管理面向的是人&#xff0c;而人是动态复杂的&#xff1b;说管理是科学&#xff0c;是因为我们可以研究动态的人背后的共性需求&#xff0c;并使管理的模型、策略、工具与之契合。 在绫致时装看来…

搭建Angular并引入NG-ZORRO组件库

作者&#xff1a;baekpcyyy&#x1f41f; 1.安装node.js 注&#xff1a;安装 16.0 或更高版本的 Node.js node官网&#xff1a;https://nodejs.org/en 2.进入angular官网 https://angular.cn/guide/setup-local 新建一个文件夹 vsc打开 打开终端 1.首先安装angular手脚架…

静电放电模型中的阻容参数

依据静电放电产生原因及其对集成电路放电方式的不同&#xff0c;静电放电模型可分成以下四类模型&#xff1a;1、人体放电模型(HBM, Human-Body Model)、2、机器放电模型(MM, Machine Model)、3、器件充电模型(CDM, Charged-Device Model)、4、电场感应模型(FIM, Field-Induced…

从设计上理解JDK动态代理

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 照理说&#xff0c;动态…

【Django-DRF】md笔记第6篇:Django-DRF的视图、认证、分页和其他功能详解

本文从分析现在流行的前后端分离Web应用模式说起&#xff0c;然后介绍如何设计REST API&#xff0c;通过使用Django来实现一个REST API为例&#xff0c;明确后端开发REST API要做的最核心工作&#xff0c;然后介绍Django REST framework能帮助我们简化开发REST API的工作。 DR…

win10 tensorrt源码编译onnx

直接利用官方源码&#xff0c;如下图&#xff0c;trtexec源码在TensorRT安装目录下&#xff0c;双击trtexec.sln文件&#xff0c;使用vs2019打开源码工程。 如下图&#xff0c;以yolov8为例子&#xff0c;编译成功项目之后&#xff0c;设置命令行参数&#xff1a; --onnxd:/yo…

表单邮箱密码登录 原生+Jquery实现

文章目录 效果代码邮箱验证正则表达式HTMLCSS JS 效果 正确密码为&#xff1a;123456 点击登录按钮校验。 代码 表单校验 - CodeSandbox 邮箱验证正则表达式 /(?:[a-z0-9!#$%&*/?^_{|}~-](?:\.[a-z0-9!#$%&*/?^_{|}~-])*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1…

实现HTTP服务监听,快来试试springboot服务端接口公网远程调试

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 二. 内网穿透2.1 安装…

AND/选品机制算法/用户表设计

3大步骤总结 大步骤总结&#xff1a; 第一大步骤&#xff1a; 生成AND算法机制所需要的8个表 AND musics Works Pool Table(音乐作品池表) 需要创建表 所需归类 AND算法池 AND videos Works Pool Table(视频作品池表) 需要创建表 所需归类 AND算法池 AND image…

基于Halcon的空间域图像滤波

任务描述&#xff1a; 图为HALCON中附带的例图“particle”。图中为某种液体&#xff0c;里面悬浮了微小颗粒&#xff0c;请分析出液体中的颗粒。 案例分析&#xff1a; 图中存在两种类型的对象&#xff1a;大的明亮物体和亮度较低的小物体&#xff08;颗粒&#xff09;。图像…

Rust UI开发(一):使用iced构建UI时,如何在界面显示中文字符

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 iced的基本逻辑是&#xff1a; UI交互产生消息message&#xff0c;message传递给后台的update&#xff0c;在这个函数中编写逻辑&#xff0c;然后通过…

经典的回溯算法题leetcode全排列问题思路代码详解

目录 全排列问题 leetcode46题.全排列 leetcode47题.全排列II 对回溯算法感兴趣的朋友也可以多多支持一下我的其他文章。 回溯算法详解-CSDN博客 经典的回溯算法题leetcode组合问题整理及思路代码详解-CSDN博客 经典的回溯算法题leetcode子集问题思路代码详解-CSDN博客 …

ResizeObserver loop limit exceeded报错解决方案

前言&#xff1a; 控制台没有报错&#xff0c;但是开发Vue项目过程中一直报ResizeObserver loop limit exceeded 错&#xff0c;找到以下解决方式。在main.js文件中重写 ResizeObserver 方法。 main.js文件 &#xff08;完整版&#xff09; import { createApp } from "v…