实现一个父进程创建出多个子进程,并且父进程与每个子进程都有独立的管道
父进程可以通过管道写入指定的任务,并随机指派某个子进程通过管道读取任务并执行
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//静态指定池里的子进程数
#define childNum 15
//利用函数指针记录需要传给子进程完成的任务
typedef void(*func)();
void Delete(){
cout << getpid() << ": " << "删除任务" << endl;
sleep(1);
}
void Install(){
cout << getpid() << ": " << "安装任务" << endl;
sleep(1);
}
void Renew(){
cout << getpid() << ": " << "更新任务" << endl;
sleep(1);
}
void Pack(){
cout << getpid() << ": " << "打包任务" << endl;
sleep(1);
}
void Send(){
cout << getpid() << ": " << "发送任务" << endl;
sleep(1);
}
//保存任务的函数
void LoadFunc(vector<func>& Funcgroup){
Funcgroup.push_back(Delete);
Funcgroup.push_back(Install);
Funcgroup.push_back(Renew);
Funcgroup.push_back(Pack);
Funcgroup.push_back(Send);
}
//创建保存子进程id、写端的fd、每个池的名字的类
class Ed{
public:
Ed(const string& name, const pid_t& id, const int& writefd)
:_name(name)
,_id(id)
,_writefd(writefd)
{}
const string getname() const{
return _name;
}
const pid_t getid() const{
return _id;
}
const int getwritefd() const{
return _writefd;
}
private:
string _name;
pid_t _id;
int _writefd;
};
//读取管道里的任务码
int ReadTask(int readFd){
int code;
ssize_t s = read(readFd, &code, sizeof(code));
if(s == 4) return code;
return -1;
}
//实现保存所有创建出来的子进程信息
//并实现子进程接收任务且执行任务
void CreateChild(vector<Ed>& childgroup, vector<func>& Taskgroup){
//因为子进程会继承父进程的文件描述符表,因此从第二个子进程创建开始
//每个子进程都会打开了一个对应着上个子进程的写端
//因为父进程里对应的写端是打开的 所以再次创建子进程就会被继承下去
//因此要记录下来每一次创建子进程时打开的写端文件描述符,在下一次创建子进程后
//将子进程继承下来的文件描述符表关掉,这样所有创建出来的子进程才不会有联系
//利用数组记录子进程继承下来的写端
vector<int> DeleteFd;
//创建子进程并创建管道
for(int i = 0; i < childNum; ++i){
//创建管道
int fds[2];
int n = pipe(fds);
assert(n == 0);
pid_t id = fork();
//子进程
if(id == 0){
//在子进程被创建出来后,先将继承下来的上个子进程的写端关闭
for(int i = 0; i < DeleteFd.size(); ++i)
close(DeleteFd[i]);
//关闭子进程的写
close(fds[1]);
while(1){
//读取
int TaskNum = ReadTask(fds[0]);
//执行任务
if(TaskNum >= 0 && TaskNum < Taskgroup.size())
Taskgroup[TaskNum]();
else if(TaskNum == -1)
break;
else
cout << "没有此任务" << endl;
}
exit(0);
}
//关闭父进程的读
close(fds[0]);
//记录子进程
childgroup.push_back({"proce " + to_string(i), id, fds[1]});
//记录子进程被创建后继承下来的上一个进程的写端
DeleteFd.push_back(fds[1]);
}
}
//发送任务给子进程
void SendChild(const Ed& s, int taskNum){
cout << "send " << taskNum << " " << "to " << s.getname() << endl;
//写入任务到子进程所对应的管道文件
int n = write(s.getwritefd(), &taskNum, sizeof(taskNum));
assert(n == sizeof(int));
}
//父进程随机选择子进程和任务并发送
//参数cnt是用来计数的,如果传入时为负数则一直循环,否则计数
void SelSend(const vector<func>& Funcgroup, const vector<Ed>& childgroup, int cnt){
while(1){
if(cnt >= 0)
if(cnt-- == 0)
break;
//随机选择一个进程
int procI = rand() % childNum;
//随机选择一个任务
int taskI = rand() % Funcgroup.size();
//发送任务
SendChild(childgroup[procI], taskI);
sleep(2);
}
//如果写端退出,则关闭所有子进程对应的写端
for(int i = 0; i < childNum; ++i)
close(childgroup[i].getwritefd());
}
//回收子进程
void waitProcess(vector<Ed>& childgroup){
for(int i = 0; i < childgroup.size(); ++i){
waitpid(childgroup[i].getid(), nullptr, 0);
cout << "等待成功: " << childgroup[i].getid() << endl;
}
}
int main(){
//随机数种子
srand(time(nullptr));
//记录所有的子进程的任务
vector<func> Funcgroup;
LoadFunc(Funcgroup);
//记录创建出来的子进程的pid
vector<Ed> childgroup;
CreateChild(childgroup, Funcgroup);
//父进程任意选择控制子进程
SelSend(Funcgroup, childgroup, 3);
//回收子进程
waitProcess(childgroup);
return 0;
}
以下动图结果演示为计数器是3的情况,因此只会有3次读写
实现一个写端进程负责发送数据,运行多个读端进程,并且写端进程与每个读端进程都有独立的命名管道
写端进程可以随机向某个管道中写入数据后,管道对应的读端将数据读取输出
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//宏定义管道名字
#define NAME_PIPE_1 "./NAME_PIPE_1"
#define NAME_PIPE_2 "./NAME_PIPE_2"
#define NAME_PIPE_3 "./NAME_PIPE_3"
#define NAME_PIPE_4 "./NAME_PIPE_4"
//将管道名字保存
std::vector<std::string> NameGroup({NAME_PIPE_1, NAME_PIPE_2, NAME_PIPE_3, NAME_PIPE_4});
//创建管道函数
bool CreatPipe(const std::string& path){
umask(0);
int n = mkfifo(path.c_str(), 0666);
if(n == 0)
return true;
else{
std::cout << "管道创建失败:" << strerror(errno) << std::endl;
return false;
}
}
//删除管道函数
void RemovePipe(const std::string& path){
assert(unlink(path.c_str()) == 0);
}
#include"NAME_PIPE.hpp"
int main(){
srand(time(nullptr));
std::vector<int> Fdgroup;
//创建所有的管道文件
for(int i = 0; i < NameGroup.size(); ++i)
assert(CreatPipe(NameGroup[i]));
//打开所有的管道文件,并将所有的管道文件fd保存
for(int i = 0; i < NameGroup.size(); ++i){
int fd = open(NameGroup[i].c_str(), O_WRONLY);
if(fd < 0)
return 1;
Fdgroup.push_back(fd);
}
std::string str;
while(1){
//随机生成一个下标,往该下标的管道文件中写入
int i = rand() % Fdgroup.size();
std::cout << "开始写入:";
getline(std::cin, str);
int s = write(Fdgroup[i], str.c_str(), strlen(str.c_str()));
}
//关闭所有管道文件
for(int i = 0; i < Fdgroup.size(); ++i)
close(Fdgroup[i]);
return 0;
}
#include"NAME_PIPE.hpp"
int main(){
//打开管道文件只读
int fd = open(NAME_PIPE_1, O_RDONLY);
assert(fd > 0);
//阻塞着随时等待管道文件中有数据,一有数据立刻读取
while(1){
char buff[1024];
int s = read(fd, buff, sizeof(buff) - 1);
if(s > 0)
buff[s] = 0;
else
break;
std::cout << "RProc1 read : " << buff << std::endl;
}
close(fd);
//删除对应的管道文件
RemovePipe(NAME_PIPE_1);
return 0;
}
#include"NAME_PIPE.hpp"
int main(){
int fd = open(NAME_PIPE_2, O_RDONLY);
assert(fd > 0);
while(1){
char buff[1024];
int s = read(fd, buff, sizeof(buff) - 1);
if(s > 0)
buff[s] = 0;
else
break;
std::cout << "RProc2 read : " << buff << std::endl;
}
close(fd);
RemovePipe(NAME_PIPE_2);
return 0;
}
#include"NAME_PIPE.hpp"
int main(){
int fd = open(NAME_PIPE_3, O_RDONLY);
assert(fd > 0);
while(1){
char buff[1024];
int s = read(fd, buff, sizeof(buff) - 1);
if(s > 0)
buff[s] = 0;
else
break;
std::cout << "RProc3 read : " << buff << std::endl;
}
close(fd);
RemovePipe(NAME_PIPE_3);
return 0;
}
#include"NAME_PIPE.hpp"
int main(){
int fd = open(NAME_PIPE_4, O_RDONLY);
assert(fd > 0);
while(1){
char buff[1024];
int s = read(fd, buff, sizeof(buff) - 1);
if(s > 0)
buff[s] = 0;
else
break;
std::cout << "RProc4 read : " << buff << std::endl;
}
close(fd);
RemovePipe(NAME_PIPE_4);
return 0;
}