如何查看网站是用什么模板做的,商城多用户源码,做商业地产常用的网站,我的电脑做网站服务器吗文章目录 为什么要有进程间通信pipe函数共享管道原理管道特点管道的四种情况 管道的应用场景#xff08;进程池#xff09;ProcessPool.ccTask.hpp 为什么要有进程间通信
数据传输#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享#xff1a;多个进程之间共享… 文章目录 为什么要有进程间通信pipe函数共享管道原理管道特点管道的四种情况 管道的应用场景进程池ProcessPool.ccTask.hpp 为什么要有进程间通信
数据传输一个进程需要将它的数据发送给另一个进程 资源共享多个进程之间共享同样的资源。 通知事件一个进程需要向另一个或一组进程发送消息通知它它们发生了某种事件如进程终止时要通知父进程。 进程控制有些进程希望完全控制另一个进程的执行如Debug进程此时控制进程希望能够拦截另一个进程的所有陷入和异常并能够及时知道它的状态改变。
pipe函数
通过pipe函数实现两个进程间的通信 pipe()函数作用生成两个文件描述符分别为读端和写端 参数
输出型参数pipefd[2]返回值pipefd[0]为读端pipefd[1]为写端 返回值 成功返回0失败返回-1并且设置错误码error
共享管道原理
通过fork函数实现父子之间的管道共享同一进程fork出的多个进程之间都可以进行管道共享因此只要是亲戚就可以。 管道共享更确切的说应该是缓冲区共享我们先来理解一下fork函数一个进程fork出了子进程两个进程之间的代码是共享的数据是独有的当其中一个进程的数据发生改变时就会发生写时拷贝。那么文件缓冲区呢 父子之间的文件缓冲区也是共享的因此父子之间就是借助这一点进行通信的。 我们以3号文件描述符为读端4号文件描述符为写端为例父进程向3号文件描述符写子进程将数据写入到4号文件描述符。而4号文件描述符读到的就是父进程向3号文件描述符写的数据这是怎么实现的呢 1.父子进程是同步的 2.父子之间缓冲区是共享的。 因此当父亲向缓冲区写的时候子进程就直接从缓冲区内读 你可能会有疑问操作系统为什么要搞出管道要是上面那样的话和父进程直接向一个文件写子进程从这个文件里读有什么区别 管道通信是加载在内存上的管道本身是一块缓冲区这种方式更快因为对于文件而言它是在磁盘上加载的如果单纯的对一个文件进行读写操作肯定是要慢一些的 为什么说这种管道通信只能应用于亲戚之间呢 因为只有亲戚之间也就是同一个进程fork出的进程之间才会进行缓冲区共享
#include iostream
#include unistd.h
#include error.h
#include stdio.h
#include cstring
#include sys/wait.h#define N 2
#define NUM 1024using namespace std;void Writer(int wfd)
{string s i am a child abcdefg;char buf[NUM];buf[0] 0;snprintf(buf, sizeof(buf), %s, s.c_str());//把s.c_str()以字符串形式写入到buf里write(wfd, buf, sizeof(buf));//write(wfd, buf, sizeof(s.c_str()));// cout sizeof(s.c_str()): sizeof(s.c_str()) endl;//s.c_str()返回值为char类型的指针// cout strlen(buf): strlen(buf) endl;// cout strlen(s.c_str()): strlen(s.c_str()) endl;
}void Reader(int rfd)
{char buf[NUM];ssize_t n read(rfd, buf, sizeof(buf));//sizeof ! strlenbuf[n] 0;//0 \0 cout buf endl;//cout n endl;//printf(%s\n, buf);
}int main()
{int pipefd[N] {0};//pipefd[2]int n pipe(pipefd);//给pipe()函数传递一个数组返回的数组下标0位置是读的文件描述符下标1位置为写的文件描述符//cout pipefd[0] pipefd[1] endl;pid_t id fork();if(id 0){perror(fork error);return 1;}if(id 0)//child --- 我们让子进程写父进程读{close(pipefd[0]);//关闭子进程的读文件描述符Writer(pipefd[1]);close(pipefd[1]);//可关可不关因为进程结束它会自动关闭exit(0);}if(id 0)//father----我么让父进程读,子进程写{close(pipefd[1]);//关闭父进程的写文件描述符Reader(pipefd[0]); }pid_t rid waitpid(id, nullptr, 0);//return id -------- ridclose(pipefd[0]);//可关可不关因为进程结束它会自动关闭return 0;
}管道特点
1.只能用于具有共同祖先的进程具有亲缘关系的进程之间进行通信通常一个管道由一个进程创建然后该进程调用fork此后父、子进程之间就可应用该管道。 2.管道提供流式服务 3.一般而言进程退出管道释放所以管道的生命周期随进程 4.一般而言内核会对管道操作进行同步与互斥管道是半双工的数据只能向一个方向流动需要双方通信时需要建立起两个管道
管道的四种情况
1.读写端正常管道如果为空读端就要阻塞 2.读写端正常管道如果被写满写端就要阻塞 3.读端正常读写端关闭读端就会读到0表明读到了文件pipe的结尾不会被阻塞 4.写端正常写读端关闭了。操作系统就要杀掉正在写入的进程。如何杀掉通过信号杀掉因为操作系统是不会做这种抵消浪费等类似的工作如果做了就是操作系统的bug
管道的应用场景进程池
我们知道当一个进程要执行一个事情时一般它会创建一个子进程并把这件事交给该进程让它去完成现在我们有若干个任务要去让这个进程去完成因此该进程就要去创建多个子进程让他们分别去完成这些事但是像这种每一次都要创建子进程的过程是很浪费时间的操作系统是不会允许这种影响效率的事情发生的那么我们要怎么提高效率呢 进程池就是让为该进程提前创建好若干个子进程当有多个任务来的时候就让这个父进程给子进程去派发不同的任务。我们以父进程为老板子进程为打工人的场景来模拟具体实现如下
ProcessPool.cc
#include unistd.h
#include string
#include iostream
#include vector
#include sys/wait.h
#include ctime
#include Task.hppusing namespace std;#define processnum 10
#define N 2
#define NUM 1024
vectortask_t tasks;//声明Task.hpp中的变量//先描述
class channel//管道
{
public:channel(int cmdfd, pid_t slaverid, const string processname):_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname){}public:int _cmdfd;//发送任务的文件描述符pid_t _slaverid;//该子进程的pidstring _processname;//子进程的名字
};void slaver()
{// char buf[NUM];// read(0, buf, sizeof(buf));int cmdnum 0;//几号任务read(0, cmdnum, sizeof(int));//cout 读到了: cmdnum endl;if(cmdnum 0 cmdnum tasks.size()){//cout cmdnum: cmdnum endl;tasks[cmdnum - 1]();//为什么要加括号?//cout 读到了: cmdnum endl;}}void Menu()
{cout ******************************* endl;cout ********1.开机 2.打怪兽****** endl;cout ********3.回血 4.关机******** endl;cout ******************************* endl;cout 请输入要执行的任务 endl;
}void InitChannels(vectorchannel* channels)
{for(int i 0; i processnum; i){int pipefd[N] {0};pipe(pipefd);//cout pipefd[0]: pipefd[0] pipefd[1]: pipefd[1] endl;pid_t pid fork();if(pid 0){close(pipefd[1]);dup2(pipefd[0], 0);slaver();//slaver(pipefd[0]);//close(pipefd[0]);//子进程读的文件描述符可以不用关exit(0);}//fatherclose(pipefd[0]);//write(pipefd[1], abcd, sizeof(abcd));//Writer();string name process: to_string(i);channels-push_back(channel(pipefd[1], getpid(), name));//close(pipefd[1]);//waitpid(getpid(), nullptr, 0);}
}void Print(vectorchannel channels)
{int i 0;for(auto e : channels){cout e._cmdfd e._processname e._slaverid endl;//cout xxxxxxxxxxxxxxxxxxx i xxxxxxxxxxxxxxxxxxxxx endl;i;}
}void ctrlSlaver(vectorchannel channels)
{while(1){//1.选择任务Menu();int select 0; cin select;//2.选择进程srand(time(nullptr));int processpos rand() % channels.size();//进程vector中对应的下标位置//3.发送任务write(channels[processpos]._cmdfd, select, sizeof(int));//cout channels[processpos]._cmdfd endl;sleep(1);}
}void QuitProcess(const std::vectorchannel channels)
{for(const auto c : channels) close(c._cmdfd);// sleep(5);for(const auto c : channels) waitpid(c._slaverid, nullptr, 0);// sleep(5);
}int main()
{LoadTask(tasks);vectorchannel channels;//1.初始化channelsInitChannels(channels);//Print(channels);//2.控制子进程ctrlSlaver(channels);QuitProcess(channels);return 0;
}Task.hpp
#pragma once#include iostream
#include vectorusing namespace std;typedef void (*task_t)();//task_t先和*结合所以task_t是一个指向参数为空返回值为void的函数指针void task1()
{cout 开机 endl;
}void task2()
{cout 打怪兽 endl;
}void task3()
{cout 回血 endl;
}void task4()
{cout 关机 endl;
}void LoadTask(vectortask_t *tasks)
{tasks-push_back(task1);tasks-push_back(task2);tasks-push_back(task3);tasks-push_back(task4);
}