wordpress付费开通站点,企业邮箱注册去哪,黄骅港海关,网络管理软件有哪些目录
管道
pipe创建一个管道
让子进程写入#xff0c;父进程读取
如何把消息发送/写入给父进程
父进程该怎么读取呢
管道本质
结论#xff1a;管道的特征#xff1a;
测试管道大小
写端退了#xff0c;测试结果
测试子进程一直写#xff0c;父进程读一会就退出 …目录
管道
pipe创建一个管道
让子进程写入父进程读取
如何把消息发送/写入给父进程
父进程该怎么读取呢
管道本质
结论管道的特征
测试管道大小
写端退了测试结果
测试子进程一直写父进程读一会就退出
管道到的应用场景
写一个进程池(pipe_use)
思路步骤
管道创建
创建父子进程
父进程写子进程读 子进程pid有了管道也有了就差在父进程添加字段了
先更改一下在class里构造一下
添加字段
把初始化改造成函数
debug测试函数纯输入函数
第二步开始控制进程了想让子进程做什么
改变一下直接从键盘0号描述符里读不从管道3里读了就没有管道的概念了slaver就不用传参了父进程通过管道写子进程通过标准输入读
现在开始选择任务和进程
也可以轮询选择定义一个计数器弄再%等
清理收尾
bug的地方
编辑
以上是匿名管道
总文件总代码
makefile中代码
Task.hpp中代码
ProcessPool.cc中代码 管道
首先自己要用用户层缓冲区还得把用户层缓冲区拷贝到管道里从键盘里输入数据到用户层缓冲区里面然后用户层缓冲区通过系统调用write写到管道里然后再通过read系统调用被对方读端读取就要从管道拷贝到读端然后再显示到显示器上。
pipe创建一个管道
pipe的介绍
1完成这件事
看图分析 运行结果 #includeiostream
#includeunistd.h
using namespace std;
int main()
{//创建管道//先创建一个pipefd数组int pipefd[2];//用n接受一下判断是否成功int n pipe(pipefd);if(n0) return 1;//创建失败了//创建成功//测试一下文件描述符是3和4coutpipefd[0]:pipefd[0]pipefd[1]:pipefd[1]endl;return 0;
} 2完成这件事 创建一个子进程 pid_t id fork();if(id 0)return 2;//创建失败if(id 0)//创建成功{//子进程}//父进程 让子进程写入父进程读取 要想让子进程进程写就需要在进程中关闭读端 if(id 0)//创建成功
{//子进程close(pipefd[0]);
}
同理 //父进程
close(pipefd[1]);
都用完结束后可以都关掉 if(id 0)//创建成功{//子进程close(pipefd[0]);//.....close(pipefd[1]);}//父进程close(pipefd[1]);//.....close(pipefd[0]);
IPC code写通信代码
3这件事也完成了 结构就有了 然后在pipefd[1]这个管道里写定义一个Writer函数 if(id 0)//创建成功{//子进程close(pipefd[0]);//.....IPC code写通信代码//在pipefd[1]这个管道里写Writer(pipefd[1]);close(pipefd[1]);exit(0);//正常退出}
同理父进程的 //父进程close(pipefd[1]);//.....IPC code写通信代码//在pipefd[0]这个管道里写Reader(pipefd[0]);close(pipefd[0]); //子进程
void Writer(int wfd)
{}
//父进程
void Reader(int rfd)
{}
Writer //子进程
void Writer(int wfd)
{string s hello,I am child;pid_t self getpid();int number 0;char buffer[10];while(true){buffer[0] 0;//字符串清空只是为了提醒阅读代码的人我把这个数组当字符串了}
} 用到snprintf 介绍
将s和self和number放进buffer char buffer[100];while(true){buffer[0] 0;//字符串清空只是为了提醒阅读代码的人我把这个数组当字符串了snprintf(buffer,sizeof(buffer),%s pid:%d\n,s.c_str(),self);cout buffer endl;sleep(1);};
用cout打印测试一下打印成功说明写入buffer成功了 等待进程少不了子进程exit后需要回收 //父进程close(pipefd[1]);//.....IPC code写通信代码//在pipefd[0]这个管道里写Reader(pipefd[0]);//等待进程缺少不了pid_t rid waitpid(id,nullptr,0);if(rid 0) return 3;//等待失败了close(pipefd[0]); 如何把消息发送/写入给父进程
用到了write 用write写入管道管道也是文件用strlen不用1不用管\0因为C语言规定\0结尾和文件没有关系wfd写入管道 //子进程
void Writer(int wfd)
{string s hello,I am child;pid_t self getpid();int number 0;char buffer[100];while(true){buffer[0] 0;//字符串清空只是为了提醒阅读代码的人我把这个数组当字符串了snprintf(buffer,sizeof(buffer),%s pid:%d %d\n,s.c_str(),self,number);//用write写入管道管道也是文件用strlen不用1不用管\0因为C语言规定\0结尾和文件没有关系wfd写入管道write(wfd,buffer,strlen(buffer));//cout buffer endl;sleep(1);};
}
父进程该怎么读取呢
用到了readfd是文件描述符从特定的文件描述符里读取放在这个buf里buf的长度是count 这里就需要考虑到\0因为buffer中需要\0 //父进程
void Reader(int rfd)
{char buffer[100];while(true){buffer[0] 0;//用sizeof是为了留个空间放\0ssize_t n read(rfd, buffer, sizeof(buffer));//sizeof!strlenif(n 0){//添加\0因为要放在buffer数组中读取buffer[n]0;cout father get a message[ getpid() ] buffer endl;}}
}
运行结果
也会发现为什么子进程sleep父进程不sleep父进程还是会跟着子进程sleep因为父子进程是要协同的 管道本质
通信是为了更好的发送变化的数据管道本质上是文件
所以必须要用到系统调用接口来访问管道其是由系统管理read和write
操作系统相当于中介
结论管道的特征
1具有血缘关系的进程进行进程间通信
2管道只能单向通信
3父子进程是会进程协同的同步与互斥的--保护管道文件的数据安全
4管道是面向字节流的
5管道是基于文件的而文件的生命周期是随进程的 再测试把子进程sleep去掉就是让子进程写快一点父进程sleep几秒就是让父进程读慢一点看有什么现象 管道的四种情况 测试管道大小
把c一直往管道里写把父进程中休眠50秒 结果差不多64kb 写端退了测试结果 结果是
读端正常读写端关闭读端就会读到0表明读到了文件pipe结尾不会被阻塞
read读取成功会返回读到的字符个数读到结尾返回0 读到结尾父进程也就可以停止读取了break后去把僵尸的子进程回收 break到这里 最后子进程会被waitpid回收 测试子进程一直写父进程读一会就退出
定义一个cnt控制退出的时间 这里也要修改一下加个sleep(5),观察close提前关闭 结果通过13号信号杀死 管道到的应用场景 都会变成一个进程 写一个进程池(pipe_use)
首先创建好文件 创建5个进程 channel通道的意思
cmdfd文件描述符
slaverid代表哪个子进程 把它放进vector容器里 思路步骤 管道创建
voidn假装使用一下要不然编译不过 创建父子进程 父进程写子进程读
子进程要读取就要关闭自己的写端父进程同理 子进程中的任务 子进程pid有了管道也有了就差在父进程添加字段了 先更改一下在class里构造一下 添加字段 测试一下结果文件描述符012是默认打开3是从管道里读4是写入管道 把初始化改造成函数 debug测试函数纯输入函数 第二步开始控制进程了想让子进程做什么 这里打印的rfd都是3正常吗文件描述符是可以被子进程继承的 父进程对应的写端拿到的是4-8子进程拿到的读端fd是3 改变一下直接从键盘0号描述符里读不从管道3里读了就没有管道的概念了slaver就不用传参了父进程通过管道写子进程通过标准输入读
用到了dup2将从pipefd[0]中读变成从0开始读 想让父进程固定的向管道里写入指定大小字节的内容必须读取四个字节四个字节四个字节的写和读这里的管道64kb
必须读取四个字节 如果父进程不给子进程发送数据呢阻塞等待! 开始控制子进程 生成一个随机数种子 可以随机选择任务和选择进程 cmd是任务码测试一下父进程控制子进程,父进程发送给子进程通过cmdcode连续 在Task.hpp里 要用到函数指针 main中的任务了就属于 再把任务装载进来 输出型参数用* 现在开始选择任务和进程 再把main中的任务弄成全局的 进行判断一下 测试 ,comcode和任创建的任务一致 这里的write是父进程进行写入向子进程发送子进程不得闲先写到管道里等得闲了再读 也可以轮询选择定义一个计数器弄再%等 整理一下控制代码这里是输入型参数只需要读 这样就可以轮询方式选择进程了不用随机了 结果 清理收尾
思路把所有文件的描述符都关掉 等待方式设置为0 read返回0就是失败了然后slaver就会调完 结束完就会exit直接退出 打印下更好显示 关闭文件描述符后sleep(10)秒 然后这10个子进程一瞬间都应该break然后最后到exit直接就退了10秒结束后父进程再回收他 测试时不弄死循环用cnt5秒后自动结束控制正常退出流程 测试结果 手动控制一下 定义一个select输入0就是退出了判断完后就走到了选择任务 然后直接把cmdcode改为选择的select-1是因为是从下标0开始的输入1就是0下标的 测试 bug的地方
这样会有一些bug一个子进程不是只有一个写端每一次子进程的创建都是有继承 这样会有一些bug一个子进程不是只有一个写端每一次子进程的创建都是有继承 按理说这样是对的可是这样就错了
因为下面两个红线还没有关掉它们进程了最开始的w 这样倒着回收是可以的 正确改法 修改一下 最后一个push_back的就都是父进程的写入fd 然后加一句这个红线的每创建子进程后都先把上一次父进程的读端fd关掉就可以了这里很妙因为vector一开始是空的 方便看 这里这样就可以了 管道已经完成 以上是匿名管道 总文件总代码 makefile中代码
ProcessPool:ProcessPool.ccg -o $ $^ -stdc11
.PHNOY:clean
clean:rm -f ProcessPool
Task.hpp中代码
#pragma once#includeiostream
#includevectorusing namespace std;typedef void (*task_t)();void task1()
{cout lol 刷新日志 endl;
}void task2()
{cout lol 更新野区 endl;
}void task3()
{cout lol 检测软件更新 endl;
}void task4()
{cout lol 释放技能 endl;
}ProcessPool.cc中代码
注意
这里为啥是取地址一个comcode不是一个0吗要发送的数据就是这个所以要把地址传给函数可以是个数组 换成数组的话read这也接收数据的时候就得用数组去接受要是写入超过int大小的话就可能会出错这个就是通信的双方要遵守的约定这个判断一下就是派发的这个任务是不是合法的假设你的tasks任务中只有4个任务所以任务编号就是0 ~ 3如果你接受到的任务编号是10或者-20那么这些就是非法的你执行的话程序就会崩溃所以要做一个简单的判断。
write以后cmdcode的值也会跟着传到read对吧write就是为了把cmdcode的值传递给给另外一个进程以前见到的都是用的char buffer[]这样cmdcode能更方便的传值过去是不看要传的是什么数据只是传递一个int数据的话就这样传递如果是文本数据或者是其他的话可能就需要数组了具体问题具体讨论
#include Task.hpp
#includeiostream
#includestring
#includevector
#includeunistd.h
#includeassert.h
#include sys/types.h
#include sys/wait.h
using namespace std;//打算创建5个进程
const int processnum 5;
//全局任务
vectortask_t tasks;//先描述
class channel//管道
{
public:channel(int cmdfd,pid_t slaverid,string processname):_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname){}public:int _cmdfd;//文件描述符pid_t _slaverid;//代表哪个子进程string _processname;//子进程的名字,方便打印日志
};//子进程中读的任务
// void slaver(int rfd)
// {
// while(true)
// {
// cout getpid() - read fd is-rfdendl;
// sleep(1000);
// }
// }
//改变一下从fd为0的地方开始读
void slaver()
{//read(0);while(true){int cmdcode 0;int n read(0, cmdcode, sizeof(int));if(n sizeof(int)){//执行cmdcode对应的任务列表cout slaver say get a command: getpid() :cmdcode: cmdcode endl;//判断一下并执行if(cmdcode 0 cmdcode tasks.size()) tasks[cmdcode]();}if(n 0) break;}
}//初始化
void Init(vectorchannel channels)
{for(int i 0;i processnum;i){int pipefd[2];int n pipe(pipefd);//创建管道//返回值小于0就创建失败了assert(!n);(void)n;pid_t id fork();if(id 0){//子进程读close(pipefd[1]);//改变一下从fd为0的地方读dup2(pipefd[0],0);close(pipefd[0]);//任务slaver();cout process: getpid() quit endl;//slaver(pipefd[0]);exit(0);}//父进程写close(pipefd[0]);//channel添加字段string name processs- to_string(i);//插入的是自定义类型要构造一下第一个传的是文件描述符要写入的fdchannels.push_back(channel(pipefd[1], id, name));}
}
//测试函数,纯输入函数
//输入const
//输出*
//输入输出
void debug(const vectorchannel channels)
{for(autoe : channels){cout e._cmdfd e._slaverid e._processnameendl;}}void Loadtask(vectortask_t *tasks)
{ tasks-push_back(task1);tasks-push_back(task2);tasks-push_back(task3);tasks-push_back(task4);
}
void memu()
{cout ######################## endl;cout 1:lol 刷新日志 2:lol 更新野区 endl;cout 1:lol 检测软件更新 4:lol 释放技能 endl;cout 0:退出 endl;cout ######################## endl;
}
//2:开始控制子进程
void ctrlSlaver(vectorchannel channels)
{int which 0;int cnt 5;while(true){int select 0;memu();cout Please Enter:;cin select;if(select 0) break;//1选择任务//int cmdcode rand()%tasks.size();int cmdcode select - 1;//2随机选择进程//int processpos rand()%channels.size();//2轮询选择进程cout father say: cmdcode: cmdcode already sendto channels[which]._slaverid process name channels[which]._processname endl;//3发送任务write(channels[which]._cmdfd, cmdcode, sizeof(cmdcode));which;which%channels.size();//保证不大于其长度cnt--;if(cnt 0) break;sleep(1);}
}
void QuitProcess(const vectorchannel channels)
{for(const auto e : channels) close(e._cmdfd);sleep(10);for(const auto e : channels) waitpid(e._slaverid, nullptr, 0);//进程的pid_slaverid关上了以后记得回收
}int main()
{Loadtask(tasks);//srand(time(nullptr)^getpid()^1023);//种一个随机数种子//在组织vectorchannel channels;//1:初始化Init(channels);debug(channels);//2:开始控制子进程ctrlSlaver(channels);//3:清理收尾QuitProcess(channels);return 0;
}