【Linux进程间通信】用管道实现简单的进程池、命名管道

【Linux进程间通信】用管道实现简单的进程池、命名管道

目录

  • 【Linux进程间通信】用管道实现简单的进程池、命名管道
    • 为什么要实现进程池?
    • 代码实现
      • 命名管道
        • 创建一个命名管道
      • 理解命名管道
        • 匿名管道与命名管道的区别
        • 命名管道的打开规则

作者:爱写代码的刚子

时间:2024.2.10

前言:本篇博客将会介绍并实现简单的线程池

为什么要实现进程池?

  • 系统调用是有成本的,池化技术是为了我们的访问速度和效率
  • 在需要频繁的创建删除较多进程的情况下,导致计算机资源消耗过多
  • 进程池则是创建指定进程数量等待执行事件,避免了不必要的创建和销毁过程

代码实现

  • ProcessPool_Task.hpp
#pragma once

#include <iostream>
#include <functional>
#include <vector>

using task_t=std::function<void()>;
//typedef void(*task_t)();
void task1()
{
    std::cout<<"task1"<<std::endl;
}

void task2()
{
    std::cout<<"task2"<<std::endl;
}

void task3()
{
    std::cout<<"task3"<<std::endl;
}

void task4()
{
    std::cout<<"task4"<<std::endl;
}

void LoadTask(std::vector<task_t> *tasks)
{
    tasks->push_back(task1);
    tasks->push_back(task2);
    tasks->push_back(task3);
    tasks->push_back(task4);

}
  • ProcessPool.cc
#include "ProcessPool_Task.hpp"
#include <string>
#include <vector>
#include <ctime>
#include <unistd.h>
#include <cstdlib>
#include <cassert>
#include <iostream>

#include <sys/wait.h>
#include <sys/stat.h>

const int processnum = 5;
//描述

std::vector<task_t> tasks;
class channel
{ 
public:
    channel(int cmdfd,int slaverid,const std::string &processname)
    :_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname)
    {}

public:
    int _cmdfd;
    pid_t _slaverid;
    std::string _processname;
};


void slaver()
{
    while(true)
    {
        int cmdcode = 0;
        int n = read(0,&cmdcode, sizeof(int));//如果父进程不给子进程发送数据,则会阻塞等待
        if(n == sizeof(int))
        {
            //执行cmdcode对应的任务列表
            std::cout <<"slaver say@ get a command: "<<getpid() << ": cmdcode: "<< cmdcode <<std::endl;
            if(cmdcode >=0 && cmdcode<tasks.size()) tasks[cmdcode]();
        }
        if(n == 0)break;
    }
}
//参数规范
//输入:const &
//输出:*
//输入输出:&


void InitProcessPool(std::vector<channel> *channels)
{
    //确保每一个子进程都只有一个写端
    std::vector<int> oldfds;
    for(int i=0;i<processnum;++i)
    {
        int pipefd[2];//临时空间
        int n = pipe(pipefd);
        assert(!n);
        (void)n;


        
        pid_t id = fork();
        if(id==0)//子进程,子进程拿到的pipefd都是3
        {
            std::cout<< "child" << getpid() << "close history fd: ";
            for(auto fd : oldfds) 
            {
                std::cout<<fd<<" ";
                close(fd);
            }
            std::cout<<"\n";
            close(pipefd[1]);
            dup2(pipefd[0],0);//将pipefd[0]重定向到0,将来直接往键盘文件(fd为0)文件里面读即可。
            close(pipefd[0]);
            
            slaver();
            std::cout<< "process : "<< getpid() << "quit" <<std::endl;
            //方法一:
            //slaver(pipefd[0]);
            exit(0);
        }
        //父进程,父进程拿到的pipefd是4,5,6...
        close(pipefd[0]);

        //添加channel字段
        std::string name = "process-"+ std::to_string(i);
        channels->push_back(channel(pipefd[1],id,name ));//pipefd[1]表示父进程要往pipefd[1]里面写
        oldfds.push_back(pipefd[1]);
    }
}

void Debug(const std::vector<channel> &channels)
{
    for(const auto &c : channels)
    {
        std::cout<<c._cmdfd<<" "<<c._slaverid<<" "<<c._processname << std::endl;
    }
}

void ctrlSlaver(const std::vector<channel> &channels)
{
    int which = 0;//轮转的方式
    while(true)
    {
        //1.选择任务
        int cmdcode = rand()%tasks.size();

        //2.选择进程
        //[负载均衡(1.随机数 2.轮转)]
        int processpos = rand()%channels.size();

        //3.发送任务
        write(channels[which]._cmdfd,&cmdcode,sizeof(cmdcode));
        
        ++which;
        which %= channels.size();
        
        sleep(1);
    }
}

void QuitProcess(const std::vector<channel> &channels)
{
    //for(const auto &c : channels) close(c._cmdfd);
    //for(const auto &c : channels) waitpid(c._slaverid,nullptr,0);
    //这里存在子进程有多个写端的问题,解决办法:
    //方法一.从后往前关闭子进程
    int last = channels.size()-1;
    for(int i= last;i>= 0;i--)
    {
        close(channels[i]._cmdfd);
        waitpid(channels[i]._slaverid,nullptr,0);
    }
    //方法二.确保每一个子进程都只有一个写端

}
int main()
{
    
    LoadTask(&tasks);
    //随机数
    srand(time(nullptr)^getpid()^1023);
    //组织
    std::vector<channel> channels;//将特定的结构转化为数据的增删查改
    //初始化
    InitProcessPool(&channels);
    Debug(channels);
    //控制子进程
    ctrlSlaver(channels);
    //清理收尾
    QuitProcess(channels);

    return 0;
}

图解:

在这里插入图片描述

命名管道

  • 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

  • 命名管道是一种特殊类型的文件

创建一个命名管道
  • $ mkfifo filename在命令行上创建命名管道

在这里插入图片描述

p开头表示这是命名管道(但是并不在磁盘上),同时管道文件的大小为0

在这里插入图片描述

  • *int mkfifo(const char filename, mode_t mode); 程序中创建命名管道的函数

理解命名管道

不同的两个进程打开同一个文件的时候,在内核中操作系统文件描述符只会指向同一个文件,进程间通信的前提:先让不同的进程看到同一份资源,管道文件则不需要进行刷盘(内存级文件),所以大小为0字节。

【问题】:如何保证打开的是同一个文件?看到同一个路径下的同一个文件名。(inode),即= 路径 + 文件名(唯一性)

匿名管道与命名管道的区别

匿名管道由pipe函数创建并打开。

命名管道由mkfifo函数创建,打开用open

FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

命名管道的打开规则
  • 如果当前打开操作是为读而打开FIFO时

    • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    • O_NONBLOCK enable:立刻返回成功
  • 如果当前打开操作是为写而打开FIFO时

    • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

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

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

相关文章

《Git 简易速速上手小册》第7章:处理大型项目(2024 最新版)

文章目录 7.1 Git Large File Storage (LFS)7.1.1 基础知识讲解7.1.2 重点案例&#xff1a;在 Python 项目中使用 Git LFS 管理数据集7.1.3 拓展案例 1&#xff1a;使用 Git LFS 管理大型静态资源7.1.4 拓展案例 2&#xff1a;优化现有项目中的大文件管理 7.2 性能优化技巧7.2.…

刘知远LLM——神经网络基础

文章目录 神经网络基础基本构成如何训练&#xff1f; Word2Vec例子负采样&#xff1a; 循环神经网络 RNN门控计算单元 GRU长短时记忆网络 LSTM遗忘门输入门输出门双向RNN卷积神经网络 CNNpytorch实战 神经网络基础 基本构成 全称&#xff1a;人工神经网络。启发于生物神经细胞…

[VulnHub靶机渗透] WestWild 1.1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

全面详细对比@Resource和@Autowired

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Resource和Autowired概述 在Java的Spring框架中&#xff0c;Resource和Autowired都是用于实现依赖注入&#xff08;Dependency Injection, DI&#xff09;的重要注解。依赖…

视频号流量真大,对新手非常友好

你好&#xff0c;我是小生&#xff0c;一个程序员转型做自媒体副业中~ 最近几天&#xff0c;做自媒体圈的同频朋友在测试视频号直播&#xff0c;目的是为了快速涨粉不违规&#xff0c;春节的流量 视频号推荐&#xff0c;这个组合非常完美。 经过测试数据看出来&#xff0c;开播…

C#,21根火柴棍问题(21 Matchticks Problem)的算法与源代码

一、21根火柴棍问题&#xff08;21 Matchticks Problem&#xff09; 21根火柴棍问题是西方经典游戏之一。 给定21根火柴&#xff0c;2个人A和B&#xff08;比如&#xff1a;分别是计算机和用户&#xff09;。 每个人一次可以挑选 1-- 4 根火柴。 被迫挑最后一根火柴的人输了…

计算机网络——网络安全

计算机网络——网络安全 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c; [跳转到网站](https://www.captainbed.cn/qianqiu) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 网络安全何…

配置DNS正反向解析服务!!!!

一.准备工作 #关闭防火墙和selinux,或者允许服务通过 [rootnode ~]# nmcli c mod ens32 ipv4.method manual ipv4.address 192.168.32.133/24 ipv4.gateway 192.168.32.2 ipv4.dns 192.168.32.132 [rootnode ~]# nmcli c reload [rootnode ~]# nmcli c up ens32[rootnode ~]# …

2.12.。

1、选择芯片型号——STM32F051K8 2、开启调试功能 3、配置时钟 4、配置时钟树 5、工程管理

基于Qt的人脸识别项目(功能:颜值检测,口罩检测,表情检测,性别检测,年龄预测等)

完整代码链接在文章末尾 效果展示 代码讲解(待更新) qt图片文件上传 #include <QtWidgets> #include <QFileDialog>

Linux diff命令

参考资料 【 diff 】コマンド&#xff08;基本編&#xff09;――テキストファイルの差分を出力する便利なdiffコマンド使い方 目录 前期准备一. 基本语法二. 文件比较2.1 无配置项2.2 -B 忽略空行&#xff0c;-w忽略空格2.3 -y 文件内容横向比较显示2.4 -q 仅显示文件是否不同…

UR10+gazebo+moveit吸盘抓取搬运demo

使用ur10gazebo开发了一个简易吸盘抓取箱子码垛的仿真过程&#xff0c;机械臂控制使用的是moveit配置。 本博客对部分关键的代码进行解释。 代码运行环境&#xff1a;支持ubuntu16、 18、 20&#xff0c; ros版本是ros1&#xff08;经过测试&#xff09;。 1、搬运场景 场景的…

单页404源码

<!doctype html> <html> <head> <meta charset"utf-8"> <title>简约 404错误页</title><link rel"shortcut icon" href"./favicon.png"><style> import url("https://fonts.googleapis.co…

Servlet验证技术

验证技术 验证是验证用户信息并确定该用户是否有权访问服务器资源的过程。用于验证用户信息的各种验证技术包括: 基本验证基于表单的验证摘要验证客户机整数验证1. 基本验证 网站可能包含两种类型的网站,即受保护和不受保护网页。默认情况下,所有用户都可以访问不受保护或者…

数据分析基础之《pandas(8)—综合案例》

一、需求 1、现在我们有一组从2006年到2016年1000部最流行的电影数据 数据来源&#xff1a;https://www.kaggle.com/damianpanek/sunday-eda/data 2、问题1 想知道这些电影数据中评分的平均分&#xff0c;导演的人数等信息&#xff0c;我们应该怎么获取&#xff1f; 3、问题…

线性代数的本质——1 向量

向量是线性代数中最为基础的概念。 何为向量&#xff1f; 从物理上看&#xff0c; 向量就是既有大小又有方向的量&#xff0c;只要这两者一定&#xff0c;就可以在空间中随便移动。 从计算机应用的角度看&#xff0c;向量和列表很接近&#xff0c;可以用来描述某对象的几个不同…

C||1.水仙花数是指一个n位数,每一位数字的n次幂的和正好等于这个数本身。2.有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数。

1.水仙花数是指一个n位数&#xff0c;每一位数字的n次幂的和正好等于这个数本身。 比如&#xff1a;153 13 53 33。 要求打印出所有三位数的水仙花数。 #include <stdio.h> #include <math.h> int main() {int i,x,y,z;for(i100;i<1000;i){xi/100%10;yi/10%…

力扣_字符串6—最小覆盖字串

题目 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 “” 。 示例 &#xff1a; 输入&#xff1a;s “ADOBECODEBANC”, t “ABC” 输出&#xff1a;“BANC” 解释&#xff1a;…

java对象内部都有哪些东西

普通对象 对象头 markword 占8字节ClassPointer 指针 :-XX userCompressedClassPointrs 为4字节&#xff0c;不开启为 8字节实例数据 引用类型: -XX userCommpressedOops 为4字节&#xff0c;不开启8字节Padding对齐&#xff0c; 8的倍数 数组对象 对象头&#xff1a;markwor…