Linux 管道理解

打印 上一主题 下一主题

主题 1835|帖子 1835|积分 5505

一、什么是管道

1.1 unix中最古老的进程间通信

1.2 一个进程链接到另一个进程的数据流称为“管道”:

图解:

二、管道通信的原理

2.1当我们创建一个进程然后打开一个文件的时候

会经过以下步骤:
①首先要描述这个进程,为这个进程创建一个task_struct的结构体对象;
②然后通过task_struct结构体里的文件描述符表指针找到这张文件描述符表;
③通过文件描述符表里的指针指向所对应打开的文件;
④文件里包含有inode文件属性、file_operators读写方法、缓冲区;
这时候就可以调用文件的读写方法对这个文件举行操作了!
图解:

2.2 当我们创建一个子进程的时候


此时子进程就能够看到父进程所看到的所有文件!!
进程通信的本质:让差异的进程看到统一份资源!
既然子进程能够看到父进程的内容了,那么父子进程之间怎么举行通信呢??
2.3 约定

想举行通讯父子进程间必须要有一个约定,要么规定父进程写入,子进程读取;大概父进程读取,子进程写入;
然而这样就行了吗?还没有,因为如果父进程是以读的方式打开的文件,子进程继承后也只能以读的方式操作这个文件而不能实现父写子读、父读子写,那怎么办呢?
2.4 方法

①父进程打开文件的时候以读文件打开一次文件,再以写方式打开一次文件(共打开两次同一文件);
②父进程fork子进程后,子进程继承父,并根据实际的场景,关闭父的读+子的写(实现父写子读)大概关闭父的写+子的读(实现父读子写)!
图解:

三、代码实现

3.1 用到的接口

①:建立管道接口
  1. #include <unistd.h>  //头文件
  2. int pipe(int pipefd[2]); //返回值小于0表示失败,pipefd 输出型参数,意思传入一个数组把文件的文件描述符带出来让用户使用!!
复制代码
记住:pipefd[0] :读下标   pipefd[1] :写下标
②:体系调用写接口
  1. #include<unistd.h> //头文件
  2. ssize_t write(int fd,void*buffer,size_t count);
  3. //fd -> 写入一个被打开的文件的描述符
  4. //buffer -> 作为缓冲区用来写入/读取文件
  5. // count -> 写入的大小
  6. //size_t 写入成功返回写入的字节数,写入失败返回-1
复制代码
③:体系调用读接口
  1. #include <unistd.h> //头文件
  2. ssize_t read(int fd,void*buffer,size_t count);
  3. //fd ->  被打开的文件的描述符(从哪读)
  4. // buffer -> 读写缓冲区 (读到哪)
  5. // count -> 读取的字节数
  6. // ssize_t -> 读取成功返回读取的字节数,读取失败返回-1,读取到文件末尾返回0
复制代码
④:fork()函数
  1. #include <unistd.h> //头文件
  2. #include <sys/types.h> //头文件
  3. 函数原型
  4.    pid_t fork( void);
  5.   (pid_t 是一个宏定义,其实质是int 被定义在#includesys/types.h>中)
  6.    返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
复制代码
⑤:snprintf()函数
  1. #include <stdio.h>//头文件
  2. int snprintf(char* str,size_t size,const char*format,...);
  3. //str -> 写到的位置
  4. // size -> 写到的位置的大小
  5. // format -> 格式字符串
  6. // ... 列表
复制代码
⑥:waitpid()函数

  1. #include <sys/types.h>
  2. #include <sys/wait.h>
  3. pid_t waitpid(pid_t pid,int *status,int options);
复制代码
3.2 linux下实现两个进程之间的管道通信

  1. //testipc1.cpp
  2. #include <unistd.h>
  3. #include <cstdio>
  4. #include <cstring>
  5. #include <sys/wait.h>
  6. #include <sys/types.h>
  7. #include <iostream>
  8. #define SIZE 1024
  9. using namespace std;
  10. void Write(int wfd)
  11. {
  12.     //写入内容
  13.     char buffer[SIZE]={0};//缓冲区
  14.     int number =0;//记录写入次数
  15.     const char*message="hello linux!! i am child!!";
  16.     while (true)
  17.     {
  18.         //子不停写
  19.         snprintf(buffer,sizeof(buffer),"%s - pid:%d -%d\n",message,getpid(),number++);
  20.         write(wfd,buffer,strlen(buffer));
  21.          sleep(2);//每隔两秒写
  22.     }
  23.    
  24. }
  25. void Read(int rfd)
  26. {
  27.     char buffer[SIZE]={0};//缓冲区
  28.     while(true)
  29.     {
  30.    ssize_t redcount= read(rfd,buffer,sizeof(buffer));
  31.    if(redcount){
  32.     buffer[redcount]='\0';
  33.    }
  34.    cout<<buffer<<endl;
  35.     }
  36. }
  37. int main()
  38. {
  39.     int pipefd[2];
  40.     int retpipe=pipe(pipefd);//建立管道
  41.     pid_t id=fork();//创建子进程
  42.     if(id<0)return 1;//创建失败
  43.     else if(id==0)
  44.     {
  45.         //child
  46.         //父读子写,子关闭读
  47.         close(pipefd[0]);
  48.         Write(pipefd[1]);
  49.         exit(0);//子写完退出程序
  50.     }
  51.     else
  52.     //farther
  53.     //父读子写,父关闭写
  54.     close(pipefd[1]);
  55.     Read(pipefd[0]);
  56.     pid_t retpid=(id,NULL,0);
  57.     if(retpid<0)return 3;//等待失败
  58.     return 0;
  59. }
复制代码
  1. //makefile文件
  2. testipc1:testipc1.cpp ##依赖目标:依赖文件
  3.         g++ -o $@ $^ -std=c++11     
  4. .PHONY:clean
  5. clean:
  6.         rm -f testipc1
复制代码
测试:

父进程乐成读取子进程写入的内容!!
四、总结

4.1、管道的五大特性

①具有血缘关系的进程;
②父子进程会协同,同步与互斥,---保护管道文件的数据安全;
③管道只能单向通信;
④管道是面向字节省的;
⑤管道是基于文件的,而文件的生命周期是随进程的;
4.2、管道的几种环境

①读写端正常,管道如果为空,读端壅闭;
②读写正常,管道写满,写端壅闭;
③读端正常,写端关闭,读端会读到0;



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

欢乐狗

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表