网站页面架构,动易学校网站,佛山市骏域网站建设,公司网站可以自己建立吗对于进程间通信的理解
首先#xff0c;进程间通信的本质是#xff0c;让不同的进程看到同一份资源#xff08;这份资源不能隶属于任何一个进程#xff0c;即应该是共享的#xff09;。而进程间通信的目的是为了实现多进程之间的协同。 但由于进程运行具有独立性#xff…对于进程间通信的理解
首先进程间通信的本质是让不同的进程看到同一份资源这份资源不能隶属于任何一个进程即应该是共享的。而进程间通信的目的是为了实现多进程之间的协同。 但由于进程运行具有独立性虚拟地址空间页表 保证了进程运行的独立性所以要实现进程间的通行难度会比较大。 管道通信作为进程间通信的一种方式Linux原生就能提供。其通信方式又分为两种匿名管道 和 命名管道。
匿名管道
匿名管道通信常用于父子进程间的通信。通过fork创建子进程让具有血缘关系的进程能够进行通信。 其实现通信的步骤主要有3步
父进程分别以读和写方式打开同一个文件fork()创建子进程父子进程各自关闭自己不需要的文件描述符 如上图看管道本质还是文件。 既然管道通信首先要能够创建出管道。pipe系统接口可以帮助创建管道。其参数pipefd是一个数组
// pipefd[0]对应读端的文件描述符
// pipefd[1]对应写端的文件描述符
int pipe(int pipefd[2]);// 匿名管道通信测试
void Test1()
{// 1.创建管道int pipefd[2] {0};int ret pipe(pipefd);if(ret ! 0){perror(pipe);exit(1);}// 测试打开的文件描述符cout pipefd[0]: pipefd[0] endl;cout pipefd[1]: pipefd[1] endl;// 2.创建子进程pid_t pid fork();if(pid 0){// 3.构建单向通行的信道父进程写入子进程读取// 3.1.父进程 -- 写// 关闭读端close(pipefd[0]);int count 0;while(true){// 不断写如变化的信息string msg hello world to_string(count);write(pipefd[1], msg.c_str(), msg.size());sleep(1);if(count 5){cout write quit endl;break;}}// 关闭写端close(pipefd[1]);// 4.等待子进程pid_t wpid waitpid(pid, nullptr, 0);if(wpid -1){perror(waitpid);exit(3);}}else if(pid 0){// 3.2.子进程 -- 读// 关闭写端close(pipefd[1]);// 不断读取信息char receive[128] {0};while(true){ssize_t size read(pipefd[0], receive, 127);if(size 0){cout receive: receive endl;}else if(size 0) {cout write quit, read quit endl;break;}else{perror(read);exit(4);}}// 关闭读端close(pipefd[0]);}else {perror(fork);exit(2);}
}通过匿名管道我们还可以模拟进程池的设计。
// 简单的进程池设计
#define PROCESS_NUM 5using f functionvoid();
unordered_mapint, f task;void load()
{task[1] [](){cout sub process[ getpid() ]-void Task1() endl;};task[2] [](){cout sub process[ getpid() ]-void Task2() endl;};task[3] [](){cout sub process[ getpid() ]-void Task3() endl;};task[4] [](){cout sub process[ getpid() ]-void Task4() endl;};
}void sendTask(int fd, pid_t pid, int task_num)
{write(fd, task_num, sizeof(task_num));cout process[ pid ] execute task task_num by fd endl;
}int waitTask(int fd)
{int task_num 0;ssize_t size read(fd, task_num, sizeof(task_num));if(size 0){return 0;}if(size sizeof(task_num)){return task_num;}return -1;
}void Test2()
{load();vectorpairint, pid_t process;// 创建多个进程for(int i 0; i PROCESS_NUM; i){// 创建管道int pipefd[2] {0};int ret pipe(pipefd);if(ret ! 0){perror(pipe);exit(1);}// 创建子进程pid_t pid fork();if(pid 0){// 子进程 -- 读close(pipefd[1]);while(true){// 等待任务int task_num waitTask(pipefd[0]);if(task_num 0){break;}else if(task_num 1 task_num task.size()){task[task_num]();}else{perror(waitTask);exit(3);}}exit(0);}else if (pid 0){perror(fork);exit(2);}// 父进程读端关闭close(pipefd[0]);process.emplace_back(pipefd[1], pid);}// 父进程 -- 写srand((unsigned int)time(0));while(true){// 选择一个进程 -- 随机数方式的负载均衡int process_num rand() % process.size();// 选择一个任务// int task_num rand() % task.size() 1;int task_num 0;cout please enter your task num: ;cin task_num;// 派发任务sendTask(process[process_num].first, process[process_num].second, task_num);}// 关闭fdfor(const auto e : process){close(e.first);}// 回收子进程for(const auto e : process){waitpid(e.second, nullptr, 0);}
}命名管道
可以用mkfifo命令创建一个命名管道。如下图是一个命名管道的小实验。 也可以通过mkfifo接口进行命名管道文件的创建。 命名管道通信的测试。
// 1. log.hpp
#include iostreamenum ErrLevel
{lev_0,lev_1,lev_2,lev_3,lev_4
};const std::string error[] {err_0,err_1,err_2,err_3,err_4
};std::ostream Log(const std::string msg, int level)
{std::cout | (unsigned int)time(0) | error[level] | msg |;return std::cout;
}// 2. comm.hpp
#include sys/types.h
#include sys/stat.h
#include wait.h
#include fcntl.h
#include unistd.h
#include cstringusing namespace std;#include log.hpp#define MODE 0666
#define SIZE 128// 命名管道通过文件路径让不同进程能看到这同一份资源
string named_pipe_path /home/zs/linux/testcpp/fifo.ipc;// 3. server.cpp
static void getMsg(int fd)
{char buffer[SIZE];while(true){memset(buffer, \0, sizeof(buffer));ssize_t size read(fd, buffer, sizeof(buffer) - 1); // ssize_t - long intif(size 0){cout [ getpid() ] client say: buffer endl;}else if(size 0){cerr [ getpid() ] read end of file, client quit, then server quit endl;break;}else{perror(read);break;}}
}void test()
{// 1.创建管道文件if(0 ! mkfifo(named_pipe_path.c_str(), MODE)){perror(mkfifo);exit(1);}Log(创建管道文件成功, lev_0) endl;// 2.文件操作int fd open(named_pipe_path.c_str(), O_RDONLY);if(fd 0){perror(open);exit(2);}Log(打开管道文件成功, lev_0) endl;for(int i 0; i 3; i){pid_t pid fork();if(pid 0){// 3.通信getMsg(fd);exit(0);}}for(int i 0; i 3; i){waitpid(-1, nullptr, 0);}// 4.关闭文件close(fd);Log(关闭管道文件成功, lev_0) endl;unlink(named_pipe_path.c_str()); // 通信完毕删除管道文件Log(删除管道文件成功, lev_0) endl;
}// 4. client.cpp
void test()
{// 1.获取管道文件int fd open(named_pipe_path.c_str(), O_WRONLY);if(fd 0){perror(open);exit(1);}// 2.通信string message;while(true){cout please enter your message: ;getline(cin, message);write(fd, message.c_str(), message.size());}// 关闭close(fd);
}管道通信总结
管道常用来进行具有血缘关系的进程间的通信管道让进程间协同提供了访问控制管道提供的是面向流式的通信服务管道是基于文件的文件的生命周期跟随进程管道的生命周期也跟随进程管道用于单向通信属于半双工通信的一种特殊情况
管道本质是文件又和传统的文件又不一样。管道文件不会将数据刷新到磁盘。 匿名管道通过父子继承的方式看到同一份资源命名管道通过文件路径的唯一性看到同一份资源从而达到不同进程间通信的目的。 对于管道文件 如果写的一方很快读的一方很慢当管道写满时写端必须等待 如果写的一方很慢读的一方很快当管道没有数据时读端必须等待 如果写端先被关闭了读端会读到文件结尾 如果读端先被关闭了操作系统会终止写端进程。