【Linux】进程池

简介: 【Linux】进程池

大致草稿

思维导图

学习目标

一、进程池的代码编写顺序

      在进程池中,我们要创建多个子进程,并且对多个子进程和父进程建立管道的关系,确保父进程和子进程之间可以进行相互通信。

      父进程就是master,而子进程就是work/slaver。大致的工作安排就是:管道中没有数据,worker进程就在阻塞等待任务的到来,而master向哪个管道写入数据,就是唤醒哪个子进程来处理任务,但是父进程要进行后端任务划分的均衡管理。

      对于每一个父进程与子进程之间的联系,我们可以建立一个类来进行描述他们之间的关系,代码如下:

class Channel
{
public:
    Channel(int wfd, int id, std::string name) // 初始化列表
        :_wfd(wfd)
        ,_subprocessid(id)
        ,_name(name)
    {}
    ~Channel()
    {}
private:
    int _wfd; // 写入的端口
    pid_t _subprocessid; // 指向的子进程的编号
    std::string _name;  // 进行连接的名字
};

1.1 创建信道和子进程

      我们可以通过使用主函数的参数来进行获取想要创建的管道数,就是在在主函数中第一个参数表示的是命令中有几个字符串,第二个参数就是存放这些字符串的数组。大致格式就是:

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cerr << "Usage:" << argv[0] << "processnum" << std::endl;
        return 1;
    }
    ......
}

      由于,在系统中,这种创建的子进程不为1,所有父进程要进行管理起来(先描述,在组织)。在前言,我们可以利用数组来进行管理这个有关管道的类。

std::vector<Channel> Channels;

      因此,现在,让我们开始进行创建信道和子进程,我们先来创建管道,定义一个文件描述符数组,我们将这个文件描述符数组传入管道函数中进行获取读写文件的端口。

int pipefd[2]; // 读端和写端的文件描述符
int n = pipe(pipefd); // 创建管道
if (n < 0)  // 如果返回值小于0,管道创建失败
{
    return;
}

      在创建完管道之后,我们开始创建子进程,但是要注意:在子进程中,不能让子进程不退出,从而导致子进程有进入循环,开始进行创建进程,这样,进程的创建就乱了套。所以,我们要在子进程中进行退出代码的编写。

      在介绍完创建管道和子进程之后,我们要来进行关闭读端或者写端,保证父进程和子进程可以通过管道实现半双工通信。代码如下:

pid_t id = fork();  // 创建子进程
if (id == 0)
{
    close(pipefd[1]);  // 关闭相应的端口
    //dup2(pipefd[0], 0);
    work(pipefd[0]);
    close(pipefd[0]);
    exit(0); 
}
 
// 3.构建一个名字
std::string channelname = "channel_" + std::to_string(i);
// 父进程
close(pipefd[0]);  // 关闭父进程
// 1.子进程的pid 2.父进程的写端
Channels->push_back(Channel(pipefd[1], id, channelname));

1.2 控制子进程,通过Channel

      在这一部分中,我们将要执行的函数建立一张函数指针数组,我们可以通过函数指针数组的下标来指向一个函数,通过数组的下标来实现函数的功能。

#define TaskNum 3
 
typedef void (* test_t)(); // 函数指针类型
 
test_t Tasks[TaskNum];  // 函数指针数组
 
 
void Loadtask()  // 加载函数,将函数指针存放在数组中
{
    srand(time(nullptr) ^ getpid() ^ 177777); // 获取当前时间作为随机数种子
    Tasks[0] = print;
    Tasks[1] = download;
    Tasks[2] = flush;
}
 
void ExcuteTask(int num)  //  启动函数
{
    if(num < 0 || num > 2)
    {
        return;
    }
    Tasks[num]();
}
 
int slectTask() // 随机发送一个码
{
    return rand() % TaskNum;
}

      因此,我们在利用管道进行控制子进程中,我们可以使用传递数组下标的方式来进行使用相应的子进程完成派遣得到的一些任务。

在控制子进程这一部分,我们可以分为:选择一个任务,选择一个进程,发送任务。

int selectChannel(int channelnum) // 利用循环的方式进行任务的分配
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}
 
void sendTask(Channel &channel, int taskcommend)
{
    // 向写端发送任务的编号
    // sleep(1);
    write(channel.getwid(), &taskcommend, sizeof taskcommend);
}
 
// 1 选择一个任务,
int taskcommend = slectTask();
 
// 2 选择一个进程,
int channelindex = selectChannel(Channels.size());
 
// 3 发送任务
sendTask(Channels[channelindex], taskcommend);

      这一部分还可以通过控制派遣任务的数量来给子进程分配任务,我们可以将代码进行改编,创建一个新的函数,我们可以在添加一个新的参数来指明派遣任务的数量。

void ctrlProcessOnce(std::vector<Channel> Channels)
{
    sleep(1);
    // 1 选择一个任务,
    int taskcommend = slectTask();
    // 2 选择一个进程,
    int channelindex = selectChannel(Channels.size());
    // 3 发送任务
    sendTask(Channels[channelindex], taskcommend);
 
    std::cout << "taskcommend:" << taskcommend << ' ' << "channel:" << Channels[channelindex].getname() << ' ' << "to send subprocess:" << Channels[channelindex].getid() << std::endl;
}
 
void ctrlProcess(std::vector<Channel> Channels, int times = -1)
{
    if (times == -1)
    {
        while (true)
        {
            ctrlProcessOnce(Channels);
        }
    }
    else
    {
        while (times--)
        {
            ctrlProcessOnce(Channels);
        }
    }
}

1.3 回收所有的管道和子进程

这一部分有一个错误,

void cleanChannel(std::vector<Channel> Channels)
{
    for (auto k : Channels)
    {
        k.CloseChannel();
    }
 
    for (auto k : Channels)
    {
        k.waitChannel();
    }
}


相关文章
|
7月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
5月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
224 67
|
4月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
105 16
|
4月前
|
Unix Linux
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
89 20
|
9月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
255 1
|
3月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
70 0
|
3月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
90 0
|
3月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
60 0
|
3月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
62 0
|
6月前
|
存储 Linux 调度
【Linux】进程概念和进程状态
本文详细介绍了Linux系统中进程的核心概念与管理机制。从进程的定义出发,阐述了其作为操作系统资源管理的基本单位的重要性,并深入解析了task_struct结构体的内容及其在进程管理中的作用。同时,文章讲解了进程的基本操作(如获取PID、查看进程信息等)、父进程与子进程的关系(重点分析fork函数)、以及进程的三种主要状态(运行、阻塞、挂起)。此外,还探讨了Linux特有的进程状态表示和孤儿进程的处理方式。通过学习这些内容,读者可以更好地理解Linux进程的运行原理并优化系统性能。
206 4