匿名管道实例--进程控制【Linux】

前言

在学习了匿名管道的基本内容之后,你有没有想过自己使用一下匿名管道,本文就详细介绍一个匿名管道的实例–进程控制。
匿名管道详解:进程间通信–匿名管道【Linux】

一、实例分析

本实例想通过管道实现一个功能 – 父进程控制子进程完成对应的任务

简而言之,就是父进程通过管道给子进程发送任务代码,然后子进程完成对应的任务,可以分为以下几个步骤。

①创建管道
②创建子进程
③实现管道通信
④父进程发送任务代码,子进程完成任务

二、管道

什么是管道

  • 管道是Unix中最古老的进程间通信的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

原理

父进程指向一个管道文件,子进程继承父进程的指向关系,从而子进程也指向管道,进行通信。
在这里插入图片描述

三、实例代码

0. 加载任务列表

由于我们需要指派子进程完成任务,首先需要一些任务列表,本实例不详细实现任务,只是打印输出执行任务成功,来模拟实现完成任务。

typedef void (*functor)();

一个数组指针,用来保存任务执行的函数。

vector<functor> functors;

一个方法集合,用vector来存放一系列的任务执行的数组指针。

unordered_map<uint32_t, string> info;

存放uint32_t类型的key值以及string字符串,存放任务名称。
其中uint32_tunsighed int的别名,为无符号整形。

生成任务

typedef void (*functor)();

vector<functor> functors; // 方法集合
// for debug
unordered_map<uint32_t, string> info;

void f1()
{
    cout << "这是一个处理日志的任务,执行的进程 ID [" << getpid() << "]"
         << ", 执行时间是 [" << time(nullptr) << "]" << endl;
}
void f2()
{
    cout << "这是一个备份数据的任务,执行的进程 ID [" << getpid() << "]"
         << ", 执行时间是 [" << time(nullptr) << "]" << endl;
}
void f3()
{
    cout << "这是一个处理网络链接的任务,执行的进程 ID [" << getpid() << "]"
         << ", 执行时间是 [" << time(nullptr) << "]" << endl;
}

void loadFunctor()
{
    info.insert({functors.size(), "处理日志的任务"});
    functors.push_back(f1);

    info.insert({functors.size(), "备份数据的任务"});
    functors.push_back(f2);

    info.insert({functors.size(), "处理网络链接的任务"});
    functors.push_back(f3);
}

生成任务之后,在main函数中使用loadFunctor函数来加载任务列表。

1. 创建管道

生成管道的读端和写端

int pipefd[2] = {0};
if (pipe(pipefd) != 0)
{
    cerr << "pipe error" << endl;
    return 1;
}

2. 创建子进程

pid_t id = fork();
if (id < 0)
{
    cerr << "fork error" << endl;
    return 2;
}

3. 子进程

3.1 关闭文件fd

close(pipefd[1]);

3.2 业务处理

子进程先读取父进程发出的指令,再执行相关任务。

assert(s == sizeof(uint32_t));
(void)s;

assert断言,是编译有效 (debug 模式),在release 模式下,断言就失效了。一旦断言失效,s变量就是只被定义,没有被使用,release模式中,可能会有warning。

while (true)
{
    uint32_t operatorType = 0;
    // 如果有数据,就读取,如果没有数据,就阻塞等待,等待任务的到来
    ssize_t s = read(pipefd[0], &operatorType, sizeof(uint32_t));
    if (s == 0)
    {
        cout << "我要退出啦,我是给人打工的,老板都走了..." << endl;
        break;
    }
    assert(s == sizeof(uint32_t));
    (void)s;
    if (operatorType < functors.size())
    {
        functors[operatorType]();
    }
    else
    {
        cerr << "bug? operatorType = " << operatorType << endl;
    }
}

执行完毕之后关闭读端并退出

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

4. 父进程

4.1 关闭文件fd

设置随机数种子并关闭不需要的文件fd

srand((long long)time(nullptr));
close(pipefd[0]);

4.2 指派任务

父进程形成任务码并向指定的进程下达执行任务的操作。执行完毕后,关闭写端,等待子进程退出。

int num = functors.size();
int cnt = 10;
while (cnt--)
{
    // 形成任务码
    uint32_t commandCode = rand() % num;
    cout << "父进程指派任务完成,任务是:" << info[commandCode]
         << ", 任务的编号是:" << cnt + 1 << endl;

    // 向指定的进程下达执行任务的操作
    write(pipefd[1], &commandCode, sizeof(uint32_t));
    sleep(1);
}
close(pipefd[1]);
pid_t res = waitpid(id, nullptr, 0);
if (res)
    cout << "wait success" << endl;

输出样例

在这里插入图片描述

源代码

#include <iostream>
#include <unistd.h>
#include <vector>
#include <unordered_map>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

typedef void (*functor)();

vector<functor> functors; // 方法集合
// for debug
unordered_map<uint32_t, string> info;

void f1()
{
    cout << "这是一个处理日志的任务,执行的进程 ID [" << getpid() << "]"
         << ", 执行时间是 [" << time(nullptr) << "]" << endl;
}
void f2()
{
    cout << "这是一个备份数据的任务,执行的进程 ID [" << getpid() << "]"
         << ", 执行时间是 [" << time(nullptr) << "]" << endl;
}
void f3()
{
    cout << "这是一个处理网络链接的任务,执行的进程 ID [" << getpid() << "]"
         << ", 执行时间是 [" << time(nullptr) << "]" << endl;
}

void loadFunctor()
{
    info.insert({functors.size(), "处理日志的任务"});
    functors.push_back(f1);

    info.insert({functors.size(), "备份数据的任务"});
    functors.push_back(f2);

    info.insert({functors.size(), "处理网络链接的任务"});
    functors.push_back(f3);
}

int main()
{
    // 0. 加载任务列表
    loadFunctor();

    // 1. 创建管道
    int pipefd[2] = {0};
    if (pipe(pipefd) != 0)
    {
        cerr << "pipe error" << endl;
        return 1;
    }

    // 2. 创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        cerr << "fork error" << endl;
        return 2;
    }
    else if (id == 0)
    {
        // 3. 关闭不需要的文件fd
        // child, read
        close(pipefd[1]);
        // 4. 业务处理
        while (true)
        {
            uint32_t operatorType = 0;
            // 如果有数据,就读取,如果没有数据,就阻塞等待,等待任务的到来
            ssize_t s = read(pipefd[0], &operatorType, sizeof(uint32_t));
            if (s == 0)
            {
                cout << "我要退出啦,我是给人打工的,老板都走了..." << endl;
                break;
            }
            assert(s == sizeof(uint32_t));
            // assert断言,是编译有效 debug 模式
            // release 模式,断言就没有了
            // 一旦断言没有了,s变量就是只被定义了,没有被使用,release模式中,可能会有warning
            (void)s;
            if (operatorType < functors.size())
            {
                functors[operatorType]();
            }
            else
            {
                cerr << "bug? operatorType = " << operatorType << endl;
            }
        }
        close(pipefd[0]);
        exit(0);
    }
    else
    {
        srand((long long)time(nullptr));
        // 3. 关闭不需要的文件fd
        // parent, write
        close(pipefd[0]);
        // 4. 指派任务
        int num = functors.size();
        int cnt = 10;
        while (cnt--)
        {
            // 5. 形成任务码
            uint32_t commandCode = rand() % num;
            cout << "父进程指派任务完成,任务是:" << info[commandCode]
                 << ", 任务的编号是:" << cnt + 1 << endl;

            // 向指定的进程下达执行任务的操作
            write(pipefd[1], &commandCode, sizeof(uint32_t));
            sleep(1);
        }
        close(pipefd[1]);
        pid_t res = waitpid(id, nullptr, 0);
        if (res)
            cout << "wait success" << endl;
    }
    return 0;
}

总结

本文从代码角度详细介绍了一个匿名管道的实例–进程控制,功能是父进程控制子进程完成对应的任务。大家有兴趣的话可以尝试用自己的方式实现这个功能。喜欢的话,欢迎点赞支持和关注~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值