1.pipe
int pipe(int fd[2])
创建匿名管道
pipefd是个文件描述符数组,其中pipefd[0]代表读端的fd,pipefd[1]代表写端的fd。
2.匿名管道原理:
3. 管道是半双工通信,是可以选择方向的单向通信
注意是单向的,一段不可以边写边读。
4.管道是什么?
管道的本质是内核中的缓冲区,通过内核缓冲区实现通信,命名管道的文件虽然可见于文件系统,但是只是标识符,并非通信介质。
5.进程对管道的读写有可能会阻塞。
缓冲区有具体大小,当缓冲区写满之后,写入就会堵塞。当缓冲区没有数据,读出就会阻塞。
6.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信。
7.若管道所有写段关闭,则从管道中读取完所有数据后,read会返回0。
若所有读端关闭,则继续write写入会触发异常导致进程退出。
8.管道是面向字节流的
9.管道是文件,它的生命周期随进程的退出,而退出。
例子一:
从键盘读取数据,写入管道,读取管道,写到屏幕
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
int main()
{
// 1.创建管道
int pipefd[2];
if (pipe(pipefd) != 0)
{
cerr << "pipe failed" << endl;
return 1;
}
// 2.创建子进程
pid_t pid = fork();
if (pid < 0)
{
//失败就打印
cerr << "fork failed" << endl;
}
else if (pid == 0)
{
//子进程
//读
close(pipefd[1]);
const int sz = 128;
char buf[sz];
while (1)
{
memset(buf, 0, sizeof(buf));
//从匿名pipe中读取,放入buf中
ssize_t s = read(pipefd[0], buf, sizeof(buf) - 1);
if (s > 0)
{
//读取成功
buf[s] = '\0';
cout << "读取成功,结果是:" << buf << endl;
}
else if (s == 0)
{
//读取结束
cout << "他结束,我也" << endl;
break;
}
else
{
// 防止报错
}
}
close(pipefd[0]);
exit(123);
}
else
{
//父进程
//写
close(pipefd[0]);
string str = "我是父进程,你慈祥的父亲,我的子进程你好!!!";
int n = 0;
while (n != 5)
{
//往pipefd[1]匿名管道中写
write(pipefd[1], str.c_str(), str.size());
sleep(1);
n++;
}
close(pipefd[1]);
}
//等待子进程
int status = 0;
pid_t wt = waitpid(pid, &status, 0);
if( WIFEXITED(status))
{
cout<<"等待成功"<<"退出码是:"<< WEXITSTATUS(status) <<endl;
}
return 0;
}
结果:
至于最后的进程等待,有不懂的可以移步这一篇博客。
例子二:
父进程将任务下标写入管道,子进程读管道内容来执行任务。
#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
#include <cassert>
#include <unordered_map>
using namespace std;
unordered_map<uint32_t, string> info;
typedef void (*fuc_pipe)();
vector<fuc_pipe> vfus;
void f1()
{
cout << "自毁程序已启动,执行的进程为:[" << getpid() << "]"
<< "执行时间为:" << time(nullptr) << endl;
}
void f2()
{
cout << "清理人类程序已启动,执行的进程为:[" << getpid() << "]"
<< "执行时间为:" << time(nullptr) << endl;
}
void f3()
{
cout << "屠杀模式已启动,执行的进程为:[" << getpid() << "]"
<< "执行时间为:" << time(nullptr) << endl;
}
void loadfuc()
{
info.insert({vfus.size(), "这是自毁程序"});
vfus.push_back(f1);
info.insert({vfus.size(), "这是清理人类程序"});
vfus.push_back(f2);
info.insert({vfus.size(), "这是屠杀模式"});
vfus.push_back(f3);
}
int main()
{
// 0.load fuc
loadfuc();
// 1.make pipe
int pipefd[2];
if (pipe(pipefd) != 0)
{
cout << "pipe error" << endl;
exit(1);
}
pid_t pid = fork();
if (pid < 0)
{
cout << "fork error" << endl;
exit(2);
}
else if (pid == 0)
{
// child read
close(pipefd[1]);
while (true)
{
//子进程从缓冲区中读
uint32_t opsb = 0;
ssize_t s = read(pipefd[0], &opsb, sizeof(uint32_t));
// 写端关闭,read返回值为0
if (s == 0)
{
cout << "父进程派发任务结束,地球毁灭人类灭亡,子进程退出" << endl;
close(pipefd[0]);
exit(123);
}
assert(s == sizeof(uint32_t));
(void)s;
// assert在debug模式下有效,如果在release模式下无效
//则s变量只被定义未被使用,(void)s 目的是防止warning
//下达任务
if (opsb < vfus.size())
{
vfus[opsb]();
}
else
{
cout << "opsb error" << endl;
}
}
}
else
{
// parent wirte
close(pipefd[0]);
// deliver task through rand
int n = vfus.size();
srand((long long)time(nullptr));
int cnt = 1;
while (cnt != 10)
{
//通过随机数下达命令
uint32_t taskcode = rand() % n;
cout << "父进程正在下达第" << cnt << "次任务,任务是:" << info[taskcode] << endl;
//父进程写入缓冲区
write(pipefd[1], &taskcode, sizeof(uint32_t));
sleep(1);
cnt++;
}
close(pipefd[1]);
// wait child
int status = 0;
pid_t ret = waitpid(pid, &status, 0);
if (ret)
{
cout << "wait child process " <<"exit code :"<<WEXITSTATUS(status)<< endl;
}
}
return 0;
}
结果是:
例子三:
一个进程派发任务给多个进程
代码:
#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
#include <cassert>
#include <unordered_map>
using namespace std;
unordered_map<uint32_t, string> info;
typedef void (*fuc_pipe)();
vector<fuc_pipe> vfus;
void f1()
{
cout << "自毁程序已启动,执行的进程为:[" << getpid() << "]"
<< "执行时间为:" << time(nullptr) <<endl<<endl;
}
void f2()
{
cout << "清理人类程序已启动,执行的进程为:[" << getpid() << "]"
<< "执行时间为:" << time(nullptr) <<endl<<endl;
}
void f3()
{
cout << "屠杀模式已启动,执行的进程为:[" << getpid() << "]"
<< "执行时间为:" << time(nullptr) <<endl<<endl;
}
void loadfuc()
{
info.insert({vfus.size(), "这是自毁程序"});
vfus.push_back(f1);
info.insert({vfus.size(), "这是清理人类程序"});
vfus.push_back(f2);
info.insert({vfus.size(), "这是屠杀模式"});
vfus.push_back(f3);
}
//第一个uint32_t存放:进程pid,第二个uint32_t存放:该进程对应的管道写端fd
typedef pair<uint32_t,uint32_t> gather;
const int ChdProNus = 8;
void Dothing(int fd)
{
//拿到读端fd
while(1)
{
cout<<"进程:["<<getpid()<<"]准备读取任务"<<endl;
uint32_t opsb = 0;
ssize_t sz = read(fd,&opsb,sizeof(uint32_t));
if(sz == 0)
{
break;
}
assert(sz == sizeof(opsb));
(void)sz;
if(opsb < vfus.size())
{
vfus[opsb]();
}
//cout<<"进程:["<<getpid()<<"]任务执行结束"<<endl;
}
}
void DeliveryTasks(const vector<gather> &assignMap)
{
srand((long long)time(nullptr));
int cnt = 25;
while(cnt--)
{
sleep(1);
//select which child process
uint32_t child = rand() % assignMap.size();
//select which task
uint32_t task = rand() % vfus.size();
//写入管道
write(assignMap[child].second,&task,sizeof(task));
cout<<"派发任务成功"<<endl;
}
}
int main()
{
vector<gather> assignMap;
loadfuc();
//创建ChdProNus个子进程
for(int i =0; i < ChdProNus; i++)
{
//创建管道
int pipefd[2];
pipe(pipefd);
//child read create child
pid_t pid = fork();
if(pid == 0)
{
//child
close(pipefd[1]);
Dothing(pipefd[0]);
close(pipefd[0]);
exit(1);
}
//parent
close(pipefd[0]);
gather g(pid,pipefd[1]);
assignMap.push_back(g);
}
//走到此处只有parent
//派发任务
cout<<"准备派发任务"<<endl;
DeliveryTasks(assignMap);
//回收资源
for(int i = 0; i < ChdProNus; i++)
{
if(waitpid(assignMap[i].first,nullptr,0))
{
cout<<"wait child succes"<<"its pid is :"<<assignMap[i].first<<"it is"<< i <<"th"<<endl;
close(assignMap[i].second);
}
}
}
结果: