Linux下的进程通信:匿名管道(模拟实现一个父进程与多个子进程间的通信)

1.进程间通信的目的

a.数据传输

b.资源共享

c.消息通知

d.进程控制


2.匿名管道的实现原理

我们都知道进程是存在内存中的,那么我们要实现进程通信的前提是必须在内存中找到一块共享区域。这块区域不属于父子进程中的任意一个。这片公共区域就可以称之为父子进程通信的管道。

 管道就和文件的使用方法差不多,我们只需要留下需要对它操作部分的权限。这样就可以实现父进程发送数据到管道,子进程从管道读出数据。

但是,要注意匿名管道这种通信是半双工的,只能从一边传入另一边,不可以俩边一起读或者一起写。


3.pipe函数

int arr[2]={0};
int ret=pipe(arr);

这个函数可以用来开辟一个管道。我们只需要传入一个大小为2的数组进去,返回值为0表示创建成功。arr[0]就是代表以read的方式打开管道的文件描述符fd,arr[1]就是表示以write的方式打开管道的文件描述符fd。


4.来实现一个简单的单父进程与多个子进程间的通信

a.task.h文件

#pragma once
  2 
  3 #include <iostream>
  4 #include <string>
  5 #include <vector>
  6 #include <unordered_map>
  7 #include <unistd.h>
  8 #include <functional>
  9 
 10 using namespace std;
 11 
 12 typedef function<void()> func; //打包下面的任务
 13 vector<func> callbacks; 
 14 unordered_map<int,string> mp; //用下标与任务对应起来
 15 
 16 void readWysql()
 17 {
 18   cout<<"sub process ["<<getpid()<<"] 执行数据库库访问任务\n"<<endl;
 19 }
 20 
 21 void execulerUrl()
 22 {
 23   cout<<"sub process ["<<getpid()<<"] 执行url分析\n"<<endl;
 24 }
 25 
 26 void cal()                                                                                                                              
 27 {
 28   cout<<"sub process ["<<getpid()<<"] 执行加密任务\n"<<endl;
 29 }
 30 
void save()
 32 {
 33   cout<<"sub process ["<<getpid()<<"] 执行数据持久化任务\n"<<endl;
 34 }
 35 
 36 void load() //将上述任务存入数组,并建立对应关系。
 37 {
 38   mp.insert({callbacks.size(),"进行数据库读取任务"});
 39   callbacks.push_back(readWysql);                                                                                                       
 40 
 41   mp.insert({callbacks.size(),"执行url分析"});
 42   callbacks.push_back(execulerUrl);
 43 
 44   mp.insert({callbacks.size(),"执行加密任务"});
 45   callbacks.push_back(cal);
 46 
 47   mp.insert({callbacks.size(),"执行数据持久化任务"});
 48   callbacks.push_back(save);
 49 }
 50 
 51 void ShowHandler()//如果想要手输任务建立菜单,需要的接口。
 52 {
 53   for(const auto&iter:mp)
 54     cout<<iter.first<<"\t"<<iter.second<<endl;
 55 }
 56 
 57 int handlerSize()
 58 {
 59   return callbacks.size();
 60 }

b. test.cpp文件

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Task.hpp"

#define PROCESS_NUM 5

using namespace std;

int waitCommand(int waitFd, bool &quit) //如果对方不发,我们就阻塞
{
    uint32_t command = 0;
    ssize_t s = read(waitFd, &command, sizeof(command));
    if (s == 0)
    {
        quit = true;
        return -1;
    }
    assert(s == sizeof(uint32_t));
    return command;
}

void sendAndWakeup(pid_t who, int fd, uint32_t command)
{
    write(fd, &command, sizeof(command));
    cout << "main process: call process " << who << " execute " << desc[command] << " through " << fd << endl;
}

int main()
{
    // 代码中关于fd的处理,有一个小问题,不影响我们使用,但是你能找到吗??
    load();
    // pid: pipefd
    vector<pair<pid_t, int>> slots;
    // 先创建多个进程
    for (int i = 0; i < PROCESS_NUM; i++)
    {
        // 创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;

        pid_t id = fork();
        assert(id != -1);
        // 子进程我们让他进行读取
        if (id == 0)
        {
            // 关闭写端
            close(pipefd[1]);
            // child
            while (true)
            {
                // pipefd[0]
                // 等命令
                bool quit = false;
                int command = waitCommand(pipefd[0], quit); //如果对方不发,我们就阻塞
                if (quit)
                    break;
                // 执行对应的命令
                if (command >= 0 && command < handlerSize())
                {
                    callbacks[command]();
                }
                else
                {
                    cout << "非法command: " << command << endl;
                }
            }
            exit(1);
        }
        // father,进行写入,关闭读端
        close(pipefd[0]); // pipefd[1]
        slots.push_back(pair<pid_t, int>(id, pipefd[1]));
    }
    // 父进程派发任务
    srand((unsigned long)time(nullptr) ^ getpid() ^ 23323123123L); // 让数据源更随机
    while (true)
    {
        // 选择一个任务, 如果任务是从网络里面来的?
        int command = rand() %  handlerSize();
        // 选择一个进程 ,采用随机数的方式,选择进程来完成任务,随机数方式的负载均衡
        int choice = rand() % slots.size();
        // 把任务给指定的进程
        sendAndWakeup(slots[choice].first, slots[choice].second, command);
        sleep(1);
        
        //手输入任务的方式(菜单)
        // int select;
        // int command;
        // cout << "############################################" << endl;
        // cout << "#   1. show funcitons      2.send command  #" << endl;
        // cout << "############################################" << endl;
        // cout << "Please Select> ";
        // cin >> select;
        // if (select == 1)
        //     showHandler();
        // else if (select == 2)
        // {
        //     cout << "Enter Your Command> ";
        //     // 选择任务
        //     cin >> command;
        //     // 选择进程
        //     int choice = rand() % slots.size();
        //     // 把任务给指定的进程
        //     sendAndWakeup(slots[choice].first, slots[choice].second, command);
        // }
        // else
        // {
        // }
    }

    // 关闭fd, 所有的子进程都会退出
    for (const auto &slot : slots)
    {
        close(slot.second);
    }

    // 回收所有的子进程信息
    for (const auto &slot : slots)
    {
        waitpid(slot.first, nullptr, 0);
    }
}

最后来看看结果吧(采用的是随机数的方式)

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会敲代码的运气选手^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值