【Linux】匿名管道实现简单进程池

这篇具有很好参考价值的文章主要介绍了【Linux】匿名管道实现简单进程池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、匿名管道通信的四种情况和五种特性

1.1、四种情况

  1. 管道内部没有数据且子进程不关闭自己的写端文件fd,读端(父进程)就要阻塞等待,直到管道里有数据。
  2. 管道内部被写满了且父进程(读端)不关闭自己的读端fd,写端(子进程)写满以后就要阻塞等待。
  3. 对于写端而言,如果写端不写了且关闭了写端fd,读端就会将管道中的数据读完,最后会读到返回值为0,表示读结束,类似于读到了文件的结尾。
  4. 读端关闭了,操作系统就会发送信号直接杀死进行写入的进程,因为没有读端写入也就没有了意义。

1.2、五种特性

  1. 管道自带同步机制,参照上面四种情况中的1,2,3。
  2. 具有血缘关系的进程进行通信,常见于父子。
  3. 管道是面向字节流的。
  4. 父子进程退出,管道自动释放,因为内存中的文件的生命周期是随进程的。 
  5. 管道只能进行单向通信。

二、匿名管道实现简单的进程池

        这个进程池可以分配我们想要的进程的个数,用命令行的方式来控制进程的个数,任务由我们自己定好,每次随机选择一个任务指派给一个进程去完成,进程的选派采用轮询的方式按顺序指派,这其中还有一些实现的细节,会在代码中以注释的方式给出。文章来源地址https://www.toymoban.com/news/detail-855573.html

头文件

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <vector>
#include <sys/wait.h>
#include <vector>
using namespace std;

typedef void (*work_t)(int pipefd);//pipefd为写端文件描述符
typedef void (*task_t)(int pipefd, pid_t fd);//fd为子进程pid

//三个任务
void PrintLog(int pipefd, pid_t fd)
{
    cout <<"pipefd:" << pipefd <<" fd:" << fd <<  " task:printf log task" << endl;
}

void ReloadConf(int pipefd, pid_t fd)
{
     cout <<"pipefd:" << pipefd <<" fd:" << fd <<  " task:Reload Conf" << endl;
}

void ConnectMysql(int pipefd, pid_t fd)
{
     cout <<"pipefd:" << pipefd <<" fd:" << fd <<  " task:Connect Mysql" << endl;
}

task_t task[3] = {PrintLog,ReloadConf,ConnectMysql };

//随机在三个任务中选择一个
uint32_t nextTask()
{
    return rand() % 3;
}


void worker(int pipefd)
{
    while(true)
    {
        uint32_t code = 0;
        //子进程会阻塞在此读,读到零就证明写端已经关闭了
        int n = read(0, &code, sizeof(code));

        if(n == sizeof(code))
        {
            if(code >= 3) continue;
            task[code](pipefd, getpid());
            sleep(1);
        }
        else if(n == 0)
        {
            cout << "child process quit now!" << endl;
            break;
        }
    }
}

源文件 

#include "processpool.hpp"

enum
{
    UsageError = 1,
    ArgError,
    PipeError
};

void Usage(char *proc)
{
    cout << "Usage:" << proc << " num" << endl;
}

class Channel
{
private:
    //管道保存写端文件描述符,管道名和子进程pid。
    int _wfd;
    string _name;
    pid_t _processid;

public:
    Channel(int wfd, string name, pid_t id)
        : _wfd(wfd), _name(name), _processid(id)
    {
    }

    void print()
    {
        cout << "_wfd:" << _wfd << " _name:" << _name << " _processid:" << _processid << endl;
    }

    const string name() const
    {
        return _name;
    }

    const int wfd() const
    {
        return _wfd;
    }

    const pid_t processid() const
    {
        return _processid;
    }

    void Close()
    {
        close(_wfd);
    }

    ~Channel()
    {
    }
};

//进程池
class processPool
{
private:
    int _sum_child_process;//进程池中进程的个数
    vector<Channel> _channelVect;

public:
    processPool(int sum_child_process)
        : _sum_child_process(sum_child_process)
    {
    }

    // 创建信道和子进程
    int CreateProcess(work_t work)//任务以函数指针的方式传入
    {
        vector<int> fds;
        for (int i = 0; i < _sum_child_process; i++)
        {
            //创建匿名管道
            int pipefd[2];
            int n = pipe(pipefd);
            if (n == -1)
                return PipeError;
            pid_t id = fork();
            if (id == 0)
            {
                //关闭多余的写端描述符,因为父进程在创建子进程的同时会将父进程的文件描述符表也给子进程拷贝一份,
                //这样子进程的文件描述符表就会保存了之前的子进程的写端文件描述符,必须要把之前的子进程的写端文件描述符关闭,
                //否则子进程在退出的时候会出异常
                if (!fds.empty())
                {
                    for (auto e : fds)
                        close(e);
                }
                close(pipefd[1]);
                dup2(pipefd[0], 0);//输入重定向
                work(pipefd[0]);
                exit(0);
            }
            string name = "Channel" + to_string(i);
            close(pipefd[0]);
            _channelVect.push_back(Channel(pipefd[1], name, id));
            fds.push_back(pipefd[1]);
        }
        return 0;
    }
    //发送某个任务给某个管道
    void SendTaskCode(const Channel *channel, uint32_t code)
    {
        cout << "send code: " << code << " to " << channel->name() << " sub prorcess id: " << channel->processid() << endl;
        write(channel->wfd(), &code, sizeof(code));
    }

    // 控制子进程
    void CtrlProcessPool(processPool *processPoolPtr, int cnt)//cnt表示任务的个数
    {
        while (cnt)
        {
            // 1、选择一个进程和信道
            const Channel *cur = processPoolPtr->nextChannel();
            cout << cur->name() << endl;

            // 2、选择一个任务
            uint32_t n = nextTask();

            // 派送任务
            processPoolPtr->SendTaskCode(cur, n);
            sleep(1);
            cnt--;
        }
    }

    //按顺序选择一个管道
    const Channel *nextChannel()
    {
        static int n = 0;
        n %= _sum_child_process;
        return &_channelVect[n++];
    }

    void Print()
    {
        for (auto &channel : _channelVect)
        {
            channel.print();
        }
    }

    // 退出所有子进程
    void killAll()
    {
        for (auto &Channel : _channelVect)
        {
            Channel.Close();
            pid_t rid = Channel.processid();
            pid_t quitid = waitpid(rid, nullptr, 0);
            if (rid == quitid)
                cout << rid << " quit succeed!" << endl;
        }
    }   
    ~processPool()
    {
    }
};

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return ArgError;
    }

    // 创建信道和子进程
    int sum_child_process = stoi(argv[1]);
    processPool *processPoolPtr = new processPool(sum_child_process);
    // vector<Channel> channelVect;
    processPoolPtr->CreateProcess(worker);

    srand(time(nullptr));

    // 控制子进程
    int cnt = 10;//10个任务
    processPoolPtr->CtrlProcessPool(processPoolPtr, cnt);

    // 回收子进程
    processPoolPtr->killAll();
    //processPoolPtr->Wait();

    delete processPoolPtr;
    return 0;
}

makefile

myprocesspool:processpool.cc
	g++ -o $@ $^
.PHONY:clean
clean:
	rm -f myprocesspool

到了这里,关于【Linux】匿名管道实现简单进程池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】进程间通信——进程间通信的介绍和分类、管道、匿名管道、命名管道、匿名管道与命名管道的区别

      进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中

    2024年02月05日
    浏览(40)
  • 【Linux】进程间通信(匿名管道 & 命名管道)-- 详解

    如何理解进程间通信? 进程具有独立性,所以进程想要通信难度是比较大的,成本高。 在日常生活中,通信的本质是传递信息,但站在程序员角度来看, 进程间通信的本质:让不同的进程看到同一份资源(内存空间) 。 进程间通信就是进程之间互相传递数据,那么进程间

    2024年04月28日
    浏览(37)
  • 【LInux】进程间通信 -- 匿名管道

    我们在学习进程管理,进程替换时,都强调了 进程的独立性 ,那进程间通信是什么?这好像和进程的独立性 相矛盾 吧? 那么今天,我们就来学习 进程间通信 ,和第一种通信方式 – 管道 进程间通信,并没有破坏进程的独立性这一特点,这点我们在 管道 讲解 而进程通信的

    2023年04月19日
    浏览(26)
  • Linux进程间通信【匿名管道】

    ✨个人主页: 北 海 🎉所属专栏: Linux学习之旅 🎃操作环境: CentOS 7.6 阿里云远程服务器 进程间通信简称为 IPC (Interprocess communication),是两个不同进程间进行任务协同的必要基础。进行通信时,首先需要确保不同进程之间构建联系,其次再根据不同的使用场景选择不同

    2024年02月08日
    浏览(37)
  • 【Linux】进程通信之匿名管道通信

    我们往往需要多个进程协同,共同完成一些事情。 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。

    2024年04月14日
    浏览(36)
  • Linux--进程间的通信-匿名管道

    进程间通信(IPC,Interprocess Communication)是指在不同进程之间传输数据和交换信息的一种机制。它允许多个进程在同一操作系统中同时运行,并实现彼此之间的协作 。 进程间通信方式: 管道(Pipe) : 管道是最基本的进程间通信方式 ,它是一种 半双工 的通信方式,通过管

    2024年04月14日
    浏览(32)
  • 【看表情包学Linux】IPC 进程间通信 | PIPE 管道 | 匿名管道 | 管道通信的原理 | 系统调用: pipe 接口

       🤣  爆笑 教程  👉 《看表情包学Linux》 🔥 CSDN 累计订阅量破千的火爆 C/C++ 教程的 2023 重制版,C 语言入门到实践的精品级趣味教程。 了解更多: 👉  \\\"不太正经\\\" 的专栏介绍  ← 试读第一章 订阅链接: 🔗 《C语言趣味教程》 ← 猛戳订阅! 目录 Ⅰ. 进程间通信(I

    2024年02月14日
    浏览(28)
  • 【探索Linux】—— 强大的命令行工具 P.14(进程间通信 | 匿名管道 | |进程池 | pipe() 函数 | mkfifo() 函数)

    当今计算机系统中,进程间通信扮演着至关重要的角色。随着计算机系统的发展和复杂性的增加,多个进程之间的协作变得更加必要和常见。进程间通信使得不同进程能够共享资源、协调工作、传输数据,并实现更加复杂和强大的功能。本文将深入探讨进程间的通信,以及管

    2024年02月05日
    浏览(60)
  • 【Linux后端服务器开发】管道设计

    目录 一、管道通信 二、匿名管道 1. 匿名管道通信 2. 匿名管道设计 三、命名管道 comm.hpp client.cc serve.cc 进程通信 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源 通知事件:一个进程向另一个(一组)进程发送信息,通知它们发

    2024年02月13日
    浏览(47)
  • FTP 服务器搭建(图文教程、实现匿名与用户双登录)

    1.1. 在搜索框中搜索 Windows 功能,点击启用或关闭 Windows 功能。 1.2. 勾选Internet Information Services 如下图,并点击确定完成安装。 2.1. 在搜索框中搜索防火墙和网络保护,选择并打开。 2.2. 打开允许应用通过防火墙。 2.3. 勾选 FTP 服务器。 3.1. 在搜索框中搜索 IIS,选择并打开

    2024年02月09日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包