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

news2024/10/10 20:41:31

文章目录

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

前言:

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

代码实现:

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

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

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

相关文章

Linux权限和软件包

前言 今天我们简单的介绍Linux中的两个概念&#xff0c;分别是权限和软件包。Linux中一切皆文件&#xff0c;权限无非就是限制不同的角色对文件的能不能得问题&#xff0c;软件包则是简单介绍一下Linux中安装卸载程序 权限 权限针对的对象是角色&#xff0c;首先我们先来介绍…

阿里云dataworks测试

文章目录 开始查看全局信息查看数据源信息(endpoint与project的信息)查看绑定、解绑钉钉创建、查看AccessKey(Access Key ID与Access Key Secret) 线上开发新建开发节点mysqlpython 本地开发python 程序调度 开始 参考文档&#xff1a;https://help.aliyun.com/zh/ram/user-gu…

新建的SpringBoot项目结构为空的可能问题与解决方案

问题&#xff1a; 如下图&#xff0c;创建一个名为springboot_demo05的SpringBoot项目的时候&#xff0c;发现创建的项目结构为空&#xff0c;没有下一级目录。但是在新窗口新建一个项目的时候没有这个问题。 打开Maven工具发现也没有关联Maven依赖模型 原因排查&#xff1a; …

abc371 f

F - Takahashi in Narrow Road 我们可以发现&#xff0c;每次操作后&#xff0c;对于一段变化后的区间&#xff0c;其变为了一段公差为1的等差数列&#xff0c;所以我们如果把每个值减去对应的下标&#xff0c;那么对应的区间变化后&#xff0c;都为一个相同的值&#xff0c;这…

观诺奖感言:学好数理化,都被AI打趴下!

10月8日&#xff0c;瑞典皇家科学院宣布&#xff0c;将2024年诺贝尔物理学奖授予两位人工智能先驱——约翰霍普菲尔德&#xff08;John Hopfield&#xff09;和杰弗里辛顿&#xff08;Geoffrey Hinton&#xff09;。 在接到瑞典方打来的电话后&#xff0c;Hinton还在反复确认&a…

ssm基于Javaee的影视创作论坛的设计与实现

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 目 录 III 第1章 绪论 1 1.1选题动因 1 1.2目的和意义 1 1.3论文结构安排 2 第2章 开发环境与技术 …

刷题 双指针 滑动窗口

面试经典 150 题 - 双指针 125. 验证回文串⭐️ 学会内部字母处理函数的使用 class Solution { public:bool isPalindrome(string s) {int left 0, right s.size() - 1;while (left < right) {// 处理左边字符if (!isalnum(s[left])) {left;continue;}// 处理右边字符if…

2024 kali虚拟机安装教程,分两大步骤,图文讲解(1)

第二步链接&#xff1a; 2024 kali虚拟机安装教程&#xff0c;分两大步骤&#xff0c;图文讲解&#xff08;2&#xff09;-CSDN博客 准备工作 1.kali的iso镜像文件 2.VMware Workstation Pro 虚拟机软件 正式开始 1.创建新的虚拟机&#xff0c;勾选自定义&#xff08;高级…

King3399(ubuntu文件系统)风扇驱动

该文章仅供参考&#xff0c;编写人不对任何实验设备、人员及测量结果负责&#xff01;&#xff01;&#xff01; 0 引言 文章主要介绍King3399&#xff08;ubuntu文件系统&#xff09;风扇控制&#xff08;GPIO&#xff09;&#xff0c;涉及king-rk3399.dts设备树修改&#x…

职场人情世故,你一定要学

职场上工作固然是第一位&#xff0c;但看似平淡的人际关系的经营&#xff0c;是对工作的顺利展开有重要的辅助作用&#xff0c;人来人往&#xff0c;这几条处事你必须要懂&#xff0c;否则职场上升职、加薪会吃亏。 1、 看破不拆穿。 职场上你要活的很通透的人&#xff0c;对什…

从 Reno TCP 到 Scalable TCP,HighSpeed TCP

前文 Scalable TCP 如何优化长肥管道 介绍了 Scalable TCP&#xff0c;但联系另一个类似的算法 HighSpeed TCP(简称 HSTCP)&#xff0c;就会看到一个类似从 Reno TCP 经 BIC 到 CUBIC 的路线&#xff0c;但采用了不同的策略。 Reno TCP 经 BIC 到 CUBIC 路线的核心在于 “在长…

2024年最新(AI绘画)Stable Diffusion4.9下载及安装教程.

软件介绍 Stable Diffusion 是一款在图像生成领域具有重大影响力的软件。 从工作原理上看&#xff0c;它利用深度学习的先进算法&#xff0c;构建起复杂且强大的神经网络架构。其核心在于能够解读用户输入的文本信息&#xff0c;并将这些信息转化为图像的特征与细节。 在使用…

游戏出海:跨境卖家入驻G2A详细操作指南

《黑悟空神话》在海外爆火&#xff0c;游戏出海也成为了众多游戏开发商的热门选择。在这个趋势之下&#xff0c;G2A&#xff0c;这个专注于游戏出海的电商平台&#xff0c;凭借庞大的用户群体&#xff0c;为游戏卖家提供了一个触达全球玩家的绝佳平台。 对于想要拓展海外市场的…

【HTML】制作一个简易图片轮播器

1. 轮播器效果图 1. 正常状态下每 1.5秒 自动轮播下张图片&#xff0c;轮播结束从头开始重复 2. 鼠标悬停时停止轮播&#xff0c;可以选择左右图片切换 2. HTML 结构 文档类型声明&#xff1a;<!DOCTYPE html> 声明文档类型为HTML5。HTML标签&#xff1a;<html lang…

2024 闽盾杯-黑盾赛道WP

CRYPTO 签到题-学会SM https://www.json.cn/encrypt/sm3 题目要求小写所以需要转换一下 或者脚本&#xff1a; import hashlib message "heidun2024" hash_object hashlib.new(sm3) hash_object.update(message.encode(utf-8)) hash_value hash_object.hexdigest(…

【STM32单片机_(HAL库)】4-5-3【定时器TIM】【感应开关盖垃圾桶项目】项目实现

1.项目需求 以下几个事件触发时&#xff0c;垃圾桶自动开盖&#xff0c;并伴随蜂鸣器短响一声&#xff0c;同时 LED 灯闪烁一下&#xff0c;2秒后自动关盖&#xff1a; 检测到有人靠近检测到有震动按下按键 KEY1 2.硬件 STM32单片机最小系统震动传感器模块蜂鸣器模块&#…

对象比较工具类:实现对业务的修改记录保存(对象字段差异对比)

测试 1&#xff1a;User类 Data NoArgsConstructor AllArgsConstructor public class User {FieldLabel("姓名")private String name;FieldLabel("年龄")private Integer age;FieldLabel("手机")private String phone;FieldLabel("手机号…

宠物空气净化器哪个牌子吸毛好?希喂、IAM、352真实测评

前段时间忙活了个大工程——热门宠物空气净化器大横评&#xff0c;通过多方位的测试&#xff0c;帮助不少朋友解决了宠物空气净化器的选购难题。 但我们还不局限于此&#xff0c;在从事家电行业这些年间&#xff0c;我们还花费大价钱购置一堆产品实测体验过各类大小家电产品&a…

Spring Boot教学资源库:从入门到精通

1绪 论 1.1研究背景 目前&#xff0c;在网络大环境下&#xff0c;越来越多高校开始实行网络教学&#xff0c;利用网络教学方式有利于学生更好的学习。 网络教学是指以计算机及网络为基础&#xff0c;来实现教学资源的上传、存储、传播和共享的教学手段。它是一种教学活动&#…

PyQt5 基于paho-mqtt库 实现MQTT通信

PyQt5 基于paho-mqtt库 实现MQTT通信 paho-mqtt安装paho-mqtt库综合示例错误处理 paho-mqtt paho-mqtt官网文档 安装paho-mqtt库 pip install paho-mqtt 综合示例 封装MQTT类订阅消息发布消息信号方式接收处理MQTT消息 import paho.mqtt.client as mqtt import sys import …