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);
}
}
最后来看看结果吧(采用的是随机数的方式)