Linux操作系统小项目——实现《进程池》

文章目录

  • 前言:
  • 代码实现:
  • 原理讲解:
    • 细节处理:

前言:

在前面的学习中,我们简单的了解了下进程之间的通信方式,目前我们只能知道父子进程的通信是通过匿名管道的方式进行通信的,这是因为这点我们就可以简简单单的来写一个项目————进程池

代码实现:

Task.hpp文件

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>

class Channel
{
public:
    Channel(int wfd, int child_process_id, std::string child_process_name)
        : _wfd(wfd)
        , _child_process_id(child_process_id)
        , _child_process_name(child_process_name)
    {}

    ~Channel(){}

    int Get_wfd() { return _wfd;}

    int Get_child_process_id() { return _child_process_id;}

    std::string Get_child_process_name() { return _child_process_name;}

    void CloseChannel() { close(_wfd); }
    void Wait()
    {
        pid_t rid = waitpid(_child_process_id, nullptr, 0);
        if (rid < 0)
        {
            std::cerr << "Filed to wait" << std::endl;
            exit(1);
        }
        std::cout << "Wait success!" << std::endl;
    }

private:
    int _wfd;
    int _child_process_id;
    std::string _child_process_name;
};


void task_print()
{
    std::cout << "Hey! This is task_print" << std::endl;
}

void task_load()
{
    std::cout << "Yo! This is task_load" << std::endl;
}

void task_flush()
{
    std::cout << "Dude! This is task_flush" << std::endl;
}

typedef void (*task_ptr)(void); // function pointer

task_ptr task_arr[3]; // function pointer's array

void TaskLoad()
{
    srand((unsigned int)time(NULL));
    task_arr[0] = task_print;
    task_arr[1] = task_load;
    task_arr[2] = task_flush;
    std::cout << "Task are all be loaded!" << std::endl;
    std::cout << "------------------------" << std::endl;
}

int ChoseTask()
{
    int n = rand() % 3;
    return n;
}

int PipeNumber(int times)
{
    static int _pipe = 0;
    int next = _pipe;
    _pipe++;
    _pipe %= times;
    return next;
}

void WriteToPipe(int p_number, int t_number, std::vector<Channel> Channels)
{
    static int i = 1;
    int n = write(Channels[p_number].Get_wfd(), &t_number, sizeof(t_number));
    if(n < 0) 
    {
        std::cerr << "failed to write" << std::endl;
        exit(1);
    }
    std::cout << i++ << "->the number of task '" << t_number << "' have writen in pipe ' " << p_number << "' already" << std::endl;
}

void work(int rfd)
{
    while(true)
    {
        sleep(1);
        int file_buffer = 0;
        int n = read(rfd, &file_buffer, sizeof(file_buffer));
        if(n < 0)
        {
            std::cerr << "Failed to read" << std::endl;
            exit(1);
        }
        else if(n == 0)
        {
            std::cout << "child process : " << getpid() << " quit" << std::endl;
            break;
        }
        task_arr[file_buffer]();
    }
}

processpool.cc文件

#include "Task.hpp"

void CreateChannels(int nums, std::vector<Channel> *Channels)
{
    for (int i = 0; i < nums; ++i)
    {
        // create pipe
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if (n < 0)
        {
            std::cerr << "Failed to create pipe" << std::endl;
            exit(1);
        }

        // create child process
        int pid = fork();

        if (pid == 0)
        {
            // child
            if (!Channels->empty())
            {
                std::cout << "----Before I close my pipefd[1], I have to close the wfd which inherited by the father process's struct files_struct!" << std::endl;
                for (auto &channel : *Channels)
                    channel.CloseChannel();
            }
            std::cout << "Here is child process!" << std::endl;
            close(pipefd[1]); // as child process close unnecessary wfd

            work(pipefd[0]);

            close(pipefd[0]);
            exit(0);
        }

        // father
        std::string name = "Channel No.";
        std::string Number = std::to_string(i);
        name += i;

        close(pipefd[0]); // as father process close unnecessary rfd
        Channels->push_back({pipefd[1], pid, name});
    }
}

void ControlChannels(int times, std::vector<Channel> Channels)
{
    for (int i = 0; i < times; ++i)
    {
        sleep(1);
        // chose pipe number to be writen in pipe
        int pipe_number = PipeNumber(times);

        // chose task number to write in pipe
        int task_number = ChoseTask();

        // write task number to the pointed pipe
        WriteToPipe(pipe_number, task_number, Channels);
    }
}

void CleanChannels(std::vector<Channel> Channels)
{
    for (auto &channel : Channels)
    {
        channel.CloseChannel();
        // channel.Wait();
    }

    // for (auto &channel : Channels)
    // {
    //     channel.Wait();
    // }
}

int main(int argc, char *argv[])
{
    if (argc == 1)
    {
        std::cerr << "Need more argc! need more option!" << std::endl;
        return 1;
    }

    int nums = std::stoi(argv[1]);

    std::vector<Channel> Channels;

    // load all the function
    TaskLoad();

    // 1、create channels and child process
    CreateChannels(nums, &Channels);

    // 2、control channels
    ControlChannels(nums, Channels);

    // 3、free channels and wait child process exit
    CleanChannels(Channels);

    // sleep(100);

    return 0;
}

原理讲解:

其实这个进程池项目通过画图就很好能做出来:
image-20241010102548398

细节处理:

  1. 由图片可知,未来可能我们要实现很多个任务的时候完全可以采用进程池的方式来解决,与之前处理任务不同的是,以前我们是要执行一个任务就创建一个子进程,而这个不一样,现在是提前就已经开辟好了一堆子进程。
    所以先提前创建好适量的子进程,有任务就交给子进程执行,这么做更适合多任务的并发操作,因为节省了创建子进程的操作。

  2. 未来master可以将任务发送至管道内部,以此来唤醒子进程来处理任务,但是要注意父进程不可针对的使用同一个管道,应当在多个管道直接轮询写入,这就保证了后端任务划分的负载均衡

  3. 具体的实现将任务发送至管道内部,从代码的角度来看其实就是将函数发送到管道之中,所以我们可以先创建一个函数指针数组来进行管理各个函数,未来在将任务写入至管道也仅仅需要传递下标(数字)即可。

  4. 在回收的时候我们也要额外注意!我们可以先让父进程遍历所有管道并关闭所有管道的wfd(这主要依据管道的5种特征——“若是管道的wfd关闭了,且rfd还没关闭,那么迟早会读到文件末尾,那么read系统调用就会返回0,就会结束读取”),再遍历一遍,等待每一个子进程退出。等待的原因其实是为了能够在子进程执行完后进行回收,从而避免出现“孤儿进程”。

  5. (BUG!!!!!)切记!不可以一边关闭管道的wfd再等待子进程退出,这会造成子进程阻塞!!!
    image-20241010104818857

    image-20241010105101099

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

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

相关文章

MacOS编译和安装Poco库的方法

1.从官网git下载最新的poco源代码 在/usr/local路径下创建Poco文件夹&#xff0c;克隆Poco源代码 sudo git clone -b poco-1.13.3-release https://github.com/pocoproject/poco.git2.等了一会后&#xff0c;报错啦&#xff01;&#xff01;&#xff01; error: RPC failed…

Unity3D 单例模式

Unity3D 泛型单例 单例模式 单例模式是一种创建型设计模式&#xff0c;能够保证一个类只有一个实例&#xff0c;提供访问实例的全局节点。 通常会把一些管理类设置成单例&#xff0c;例如 GameManager、UIManager 等&#xff0c;可以很方便地使用这些管理类单例&#xff0c;…

<Project-8 pdf2tx-MM> Python Flask应用:在浏览器中翻译PDF文件 NLTK OCR 多线程 指定翻译器 改进后的P6

项目概述 名字解释 缩写&#xff1a; pdf2tx-MM pdf file transfer to text content with Multi-threads and Multi-translators pdf2tx-MM 是一个基于 Flask 的 Web 应用程序&#xff0c;提供将 PDF 文件中的内容提取、翻译并展示。使用者上传 PDF 文件&#xff0c;应用程序…

计算机视觉之OpenCV vs YOLO

好多开发者希望搞明白OpenCV 和YOLO区别&#xff0c;实际上&#xff0c;二者在计算机视觉领域都有广泛应用&#xff0c;但它们有很大的不同。 一、OpenCV 概述 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它…

Cherno游戏引擎笔记(61~72)

---------------一些维护和更改------------- 》》》》 Made Win-GenProjects.bat work from every directory 代码更改&#xff1a; echo off->pushd ..\->pushd %~dp0\..\call vendor\bin\premake\premake5.exe vs2019popdPAUSE 为什么要做这样的更改&#xff1f; …

20年408数据结构

第一题&#xff1a; 解析&#xff1a;这种题可以先画个草图分析一下&#xff0c;一下就看出来了。 这里的m(7,2)对应的是这图里的m(2,7),第一列存1个元素&#xff0c;第二列存2个元素&#xff0c;第三列存3个元素&#xff0c;第四列存4个元素&#xff0c;第五列存5个元素&#…

云栖实录 | 大模型驱动,开源融合的 AI 搜索产品发布

本文根据2024云栖大会实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a; 郭瑞杰 | 阿里云智能集团资深技术专家&#xff0c;阿里云 AI 搜索负责人 邹 宽&#xff5c;阿里云智能集团高级产品专家&#xff0c;阿里云 AI 搜索产品负责人 活动&#xff1a;…

【EXCEL数据处理】000021 案例 保姆级教程,附多个操作案例。EXCEL文档安全性设置。

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】000021 案例 保姆级教程&#xff0c;附多个操作案例。…

【套路揭秘】新手如何一步跃升至管理层

&#x1f525;禅道AI功能让企业实现人工智能的自然接入&#xff0c;助力企业开启高效办公新阶段。 ✅AI 提词 通过AI一键提词&#xff0c;可灵活设计项目流程需要的AI提词&#xff0c;实现人工智能无缝融入产品研发流程&#xff0c;提高软件研发过程效率。 &#x1f4a1;需求…

uniapp打包安卓apk步骤

然后安装在手机上就可以啦

Apache DolphinScheduler-1.3.9源码分析(二)

引言 随着大数据的发展&#xff0c;任务调度系统成为了数据处理和管理中至关重要的部分。Apache DolphinScheduler 是一款优秀的开源分布式工作流调度平台&#xff0c;在大数据场景中得到广泛应用。 在本文中&#xff0c;我们将对 Apache DolphinScheduler 1.3.9 版本的源码进…

基于FPGA的ov5640摄像头图像采集(二)

之前讲过ov5640摄像头图像采集&#xff0c;但是只包了的摄像头驱动与数据对齐两部分&#xff0c;但是由于摄像头输入的像素时钟与HDMI输出的驱动时钟并不相同&#xff0c;所有需要利用DDR3来将像素数据进行缓存再将像素数据从DDR3中读出&#xff0c;对DDR3的读写参考米联客的IP…

JVM系列(二) -类的加载过程介绍

一、背景介绍 我们知道 Java 是先通过编译器将.java类文件转成.class字节码文件&#xff0c;然后再通过虚拟机将.class字节码文件加载到内存中来实现应用程序的运行。 那么虚拟机是什么时候加载class文件&#xff1f;如何加载class文件&#xff1f;class文件进入到虚拟机后发…

Python酷库之旅-第三方库Pandas(142)

目录 一、用法精讲 641、pandas.Timestamp.hour属性 641-1、语法 641-2、参数 641-3、功能 641-4、返回值 641-5、说明 641-6、用法 641-6-1、数据准备 641-6-2、代码示例 641-6-3、结果输出 642、pandas.Timestamp.is_leap_year属性 642-1、语法 642-2、参数 6…

【MySQL 08】复合查询

目录 1.准备工作 2.多表查询 笛卡尔积 多表查询案例 3. 自连接 4.子查询 1.单行子查询 2.多行子查询 3.多列子查询 4.在from子句中使用子查询 5.合并查询 1.union 2.union all 1.准备工作 如下三个表&#xff0c;将作为示例&#xff0c;理解复合查询 EMP员工表…

在IDEA里用XDebug调试PHP,断点....

做程序开发,调试必不可少,这里最近用到了PHP,顺便写个关于PHP的调试安装使用: 1、首先是PHP先安装xdebug扩展(还有zend的),这个我的工具是IDEA,所以安装方法也相对简单,如果你是用VSCode等应该也是一样,如下图,找到这个PHP->DEBUG 2、直接点上面的Install XDebug 就可以帮你…

C(十五)函数综合(一)--- 开公司吗?

在这篇文章中&#xff0c;杰哥将带大家 “开公司”。 主干内容部分&#xff08;你将收获&#xff09;&#xff1a;&#x1f449; 为什么要有函数&#xff1f;函数有哪些&#xff1f;怎么自定义函数以及获得函数的使用权&#xff1f;怎么对函数进行传参&#xff1f;函数中变量的…

springboot kafka多数据源,通过配置动态加载发送者和消费者

前言 最近做项目&#xff0c;需要支持kafka多数据源&#xff0c;实际上我们也可以通过代码固定写死多套kafka集群逻辑&#xff0c;但是如果需要不修改代码扩展呢&#xff0c;因为kafka本身不处理额外逻辑&#xff0c;只是起到削峰&#xff0c;和数据的传递&#xff0c;那么就需…

FastAPI框架使用枚举来型来限定参数、FastApi框架隐藏没多大意义的Schemes模型部分内容以及常见的WSGI服务器Gunicorn、uWSGI了解

一、FastAPI框架使用枚举来型来限定参数 FastAPI框架验证时&#xff0c;有时需要通过枚举的方式来限定参数只能为某几个值中的一个&#xff0c;这时就可以使用FastAPI框架的枚举类型Enum了。publish:December 23, 2020 -Wednesday 代码如下&#xff1a; #引入Enum模块 from fa…

Python常用的函数大全!

对Python的内置函数进行了非常详细且有条理的分组和描述。 第一组 print()&#xff1a;用于输出信息到控制台。input()&#xff1a;用于从用户那里接收输入。len()&#xff1a;返回对象&#xff08;如字符串、列表、元组等&#xff09;的长度。类型转换函数&#xff08;int()…