Linux中的进程间通讯之管道

[复制链接]
发表于 2026-1-14 23:55:19 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
管道

管道是Unix中最古老的进程间通讯的情势。
我们把从一个进程毗连到另一个进程的一个数据流称为一个“管道”

匿名管道
  1. #include <unistd.h>
  2. 功能:创建一无名管道
  3. 原型
  4. int pipe(int fd[2]);
  5. 参数
  6. fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
  7. 返回值:成功返回0,失败返回错误代码
复制代码

管道文件时一种内存级文件,没著名称,故为匿名管道
父进程打开文件然后fork创建子进程,子进程会继续父进程文件形貌符表中的内容。
然后两个进程即可看到同一份文件。

一样寻常而言管道只能用来举行单向的数据通讯。
为什么让父进程以读和写的方式打开同一个文件?
为了让子进程看到读写端。
  1. int fds[2];
  2.     int n=pipe(fds);
复制代码

代码实现 
  1.     //1.创建管道文件打开读写端   
  2. int fds[2];
  3.     int n=pipe(fds);    assert(n==0);    //2.fork    pid_t id=fork();    assert(id>=0);    if(id==0)    {        //子进程写入        close(fds[0]);        const char* s="子进程,正在向父进程发消息";        int cnt=0;        while(true)        {            cnt++;            char buffer[1024];            snprintf(buffer,sizeof buffer,"child->parent say: %s[%d][%d]",s,cnt,getpid());            write(fds[1],buffer,sizeof(buffer));//体系接口不须要思量'\0'            sleep(1);        }        close(fds[1]);        exit(0);    }    close(fds[1]);    while(true)    {        char buffer[1024];        ssize_t s=read(fds[0],buffer,sizeof(buffer)-1);        if(s>0)//s代表读到的字节数            buffer[s]=0;        cout<<"Get Message # "<<buffer<<"| mypid: "<<getpid()<<endl;    }    n=waitpid(id,nullptr,0);    assert(n==id);    close(fds[0]);
复制代码

定名管道

定名管道可以从下令行上创建,下令行方法是使用下面这个下令:
  1. mkfifo filename
复制代码
定名管道也可以从步调里创建,干系函数有:
  1. int mkfifo(const char *filename,mode_t mode);
复制代码
创建定名管道:
  1. int main(int argc, char *argv[])
  2. {
  3. mkfifo("p2", 0644);
  4. return 0;
  5. }
复制代码
comm.hpp
  1. #pragma once
  2. #include<iostream>
  3. #include<cassert>
  4. #include<cstdio>
  5. #include<cstring>
  6. #include<string>
  7. #include<unistd.h>
  8. #include<sys/wait.h>
  9. #include<sys/types.h>
  10. #include<sys/stat.h>
  11. #include<cerrno>
  12. #include<fcntl.h>
  13. using namespace std;
  14. #define NAMED_PIPE "/tmp/mypipe.test"
  15. bool createFifo(const string& path)
  16. {
  17.     int n=mkfifo(path.c_str(),0666);
  18.     if(n==0) return true;
  19.     else
  20.     {
  21.         cout<<"errno: "<<errno<<" err string: "<<strerror(errno)<<endl;
  22.         return false;
  23.     }
  24. }
  25. void removeFifo(const string& path)
  26. {
  27.     int n=unlink(path.c_str());
  28.     assert(n==0);
  29. }
复制代码
server.cc
  1. #include"comm.hpp"
  2. using namespace std;
  3. int main()
  4. {
  5.     bool r=createFifo(NAMED_PIPE);
  6.     assert(r);
  7.     cout<<"server begin"<<endl;
  8.     int rfd=open(NAMED_PIPE,O_RDONLY);
  9.     cout<<"server end"<<endl;
  10.     if(rfd<0) exit(1);
  11.     //read
  12.     char buffer[1024];
  13.     while (true)
  14.     {
  15.         ssize_t s=read(rfd,buffer,sizeof(buffer)-1);
  16.         if(s>0)
  17.         {
  18.             buffer[s]=0;
  19.             cout<<"client->server# "<<buffer;
  20.         }
  21.         else if(s==0)
  22.         {
  23.             cout<<"client quit , me too"<<endl;
  24.             break;
  25.         }
  26.         else
  27.         {
  28.             cout<<"err string--------"<<strerror(errno)<<endl;
  29.             break;
  30.         }
  31.     }
  32.    
  33.     close(rfd);
  34.     removeFifo(NAMED_PIPE);
  35.     return 0;
  36. }
复制代码
clent.cc
  1. #include"comm.hpp"
  2. using namespace std;
  3. int main()
  4. {
  5.     cout<<"client begin"<<endl;
  6.     int wfd = open(NAMED_PIPE, O_WRONLY);
  7.     cout<<"client end"<<endl;
  8.     if (wfd < 0)
  9.         exit(1);
  10.     // write
  11.     char buffer[1024];
  12.     while (true)
  13.     {
  14.         cout << "Please Say# ";
  15.         fgets(buffer, sizeof(buffer), stdin);
  16.         if(strlen(buffer)>0) buffer[strlen(buffer)-1]=0;
  17.         ssize_t n = write(wfd, buffer, strlen(buffer));
  18.         assert(n == strlen(buffer));
  19.     }
  20.     close(wfd);
  21.     return 0;
  22. }
复制代码
管道的读写特性

1. (读慢,写快)如果管道中没有了数据,读端在读,默认会直接壅闭当前正在读取的进程
2.(读快,写慢)管道是一个固定巨细的缓冲区,写端写满时会壅闭,期待对方读取
3.(写端关闭,读到0)读端将数据读完后读到0竣事进程。
4.读关闭,在写就没故意义了,OS会给写进程发送信号(13),将其制止。
管道的特性

1.管道的声明周期随进程
2.管道可以用来举行具有血缘关系的进程之间举行通讯,常用于父子通讯
3.管道是面向字节省的。按设置的最大字节数去读。
4.管道通讯---半双工
  1. sleep 1000 | sleep 2000
复制代码
|:即为匿名管道
sleep的父进程为bash
综合案例

基于匿名管道的进程池筹划

  1. #include<iostream>
  2. #include<vector>
  3. #include<cassert>
  4. #include<cstdio>
  5. #include<cstring>
  6. #include<ctime>
  7. #include<string>
  8. #include<unistd.h>
  9. #include<sys/wait.h>
  10. #include<sys/types.h>
  11. using namespace std;
  12. #define PROCESS_NUM 5
  13. #define MakeSeed() srand((unsigned long)time(nullptr))
  14. typedef void(*func_t)();
  15. void downLoadTask()
  16. {
  17.     cout<<getpid()<<" : downLoadTask()\n"<<endl;
  18.     sleep(1);
  19. }
  20. void ioTask()
  21. {
  22.     cout<<getpid()<<" : ioTask()\n"<<endl;
  23.     sleep(1);
  24. }
  25. void flushTask()
  26. {
  27.     cout<<getpid()<<" : flustTask()\n"<<endl;
  28.     sleep(1);
  29. }
  30. class subEp
  31. {
  32. public:
  33.     subEp(pid_t subId,int writeFd)
  34.         :subId_(subId),writeFd_(writeFd)
  35.     {
  36.         char nameBuffer[1024];
  37.         snprintf(nameBuffer,sizeof nameBuffer,"process-%d[pid(%d)-fd(%d)]",num++,subId_,writeFd_);
  38.         name_=nameBuffer;
  39.     }
  40. public:
  41.     static int num;
  42.     string name_;
  43.     pid_t subId_;
  44.     int writeFd_;
  45. };
  46. int subEp::num=0;
  47. void sendTask(const subEp& process,int taskNum)
  48. {
  49.     cout<<"send task num   "<<taskNum<<" send to -> "<<process.name_<<endl;
  50.     ssize_t n=write(process.writeFd_,&taskNum,sizeof(taskNum));
  51.     assert(n==sizeof(int));
  52. }
  53. int recvTask(int readFd)
  54. {
  55.     int code=0;
  56.     ssize_t s=read(readFd,&code,sizeof code);
  57.     //assert(s==sizeof(int));
  58.     if(s==4) return code;
  59.     else if(s<=0) return -1;
  60.     else return 0;
  61. }
  62. void createSubProcess(vector<subEp>* subs,vector<func_t>& funcMap)
  63. {
  64.     for(int i=0;i<PROCESS_NUM;++i)
  65.     {
  66.         int fds[2];
  67.         int n=pipe(fds);
  68.         //父进程打开的文件是会被子进程共享的
  69.         pid_t id=fork();
  70.         if(id==0)
  71.         {
  72.             //子进程
  73.             close(fds[1]);
  74.             while (true)
  75.             {
  76.                 //1.获取命令玛,如果没有,子进程应阻塞
  77.                 int commandCode = recvTask(fds[0]);
  78.                 //2.完成任务
  79.                 if(commandCode>0 && commandCode<funcMap.size()) funcMap[commandCode]();
  80.                 else if(commandCode==-1) break;;
  81.             }
  82.             exit(0);
  83.         }
  84.         close(fds[0]);
  85.         subEp sub(id,fds[1]);
  86.         subs->push_back(sub);
  87.     }
  88. }
  89. void loadTaskFunc(vector<func_t>* out)
  90. {
  91.     assert(out);
  92.     out->push_back(downLoadTask);
  93.     out->push_back(ioTask);
  94.     out->push_back(flushTask);
  95. }
  96. void loadBalanceContrl(const vector<subEp>& subs,const vector<func_t> &funcMap,int count)
  97. {
  98.     int processnum=subs.size();
  99.     int tasknum=funcMap.size();
  100.     //int cnt=subs.size();
  101.     bool forever=(count==0)?true:false;
  102.     while(true)
  103.     {
  104.         //1.选择一个子进程--->vector<subEp> -> index
  105.         int subIdx=rand()%processnum;
  106.         //2.选择一个任务---> vector<func_t>--->index
  107.         int taskIdx=rand()%tasknum;
  108.         //3.任务发送给选择的进程
  109.         sendTask(subs[subIdx],taskIdx);
  110.         sleep(1);
  111.         if(!forever)
  112.         {
  113.             count--;
  114.             if(count==0) break;
  115.         }
  116.     }
  117.     //write quit -> read 0
  118.     for(int i=0;i<processnum;++i) close(subs[i].writeFd_);
  119. }
  120. void waitProcess(vector<subEp> processes)
  121. {
  122.     int processnum=processes.size();
  123.     for(int i=0;i<processnum;++i)
  124.     {
  125.         waitpid(processes[i].subId_,nullptr,0);
  126.         cout<<"wait sub process success ..."<<processes[i].subId_<<endl;
  127.     }
  128. }
  129. int main()
  130. {
  131.     MakeSeed();
  132.     //1.建立子进程并建立和子进程通信的信道
  133.     vector<func_t> funcMap;
  134.     loadTaskFunc(&funcMap);
  135.     vector<subEp> subs;
  136.     createSubProcess(&subs,funcMap);
  137.     //2.父进程控制子进程
  138.     int taskNum=3;
  139.     loadBalanceContrl(subs,funcMap,taskNum);
  140.     //3.回收子进程信息
  141.     waitProcess(subs);
  142.     return 0;
  143. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表