Linux--进程间通信--管道

来源:互联网 发布:小米6陶瓷尊享版 知乎 编辑:程序博客网 时间:2024/05/20 15:37

Linux:进程间通信--命名管道:命名管道

1进程间通信

进程间通信,英文又称IPC(InterProcess Communication),因为每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户地址空间烤到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制就称为进程间通信。

2管道

2.1概念

管道是一种最基本的IPC机制,从本质上讲,管道也是一种文件。在LInux系统中,我们经常通过符号"|"来使用管道,用以连接两个多个命令。实际上,管道是进程与进程间的数据流通管道,它是的数据可以以一种“流”的形式在进程间流动。
管道的特点:
  1. 单向通信。
  2. 依赖文件系统。管道的生存周期随进程的退出,管道就被销毁(随进程)。
  3. 管道只能用于血缘关系的进程,通常用于父子进程。
  4. 管道在进行读写操作时,以数据流方式。
  5. 同步访问。

2.2管道的创建

Linux系统下,管道是通过pipe函数创建的,函数原型如下:
#include<inistd.h>int pipe(int fileds[2]);//成功返回0,失败返回-1
其中,参数fileds[2]是一个长度为2的文件描述符数组,fileds[0]是读端,fileds[1]是写端。

2.3管道的读写

在单个进程中创建管道几乎没有意义的,因为管道存在的意义就是进行进程之间的通信。上面我们介绍了管道的特点,其中一条是,管道只能用于有血缘关系的进程,所以我们通过父子进程之间的通信演示一下管道的使用。

说明:
父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端,父进程调用fork创建了子进程,子进程也有两个文件描述符指向同一管道。
因为管道是单向通信的,所以父进程关闭写端,子进程关闭读端。子进程可以往管道里写,父进程可以在管道里读,数据从写端流入,从写端流出,实现进程通信。
代码:
#include<stdio.h>#include<unistd.h>#include<errno.h>#include<string.h>//创建管道,子进程只写,父进程只读int main(){int fds[2];int ret=pipe(fds);if(ret==-1){printf("Create pipe error! errno code  is:%d\n",errno);return 1;}pid_t id=fork();if(id<0){printf("fork error!");return 2;}else if(id==0){//childclose(fds[0]);//关闭读char* msg="I am child!";while(1){write(fds[1],msg,strlen(msg)+1);sleep(1);}}else{close(fds[1]);//关闭写char buf[1024];while(1){read(fds[0],buf,sizeof(buf));printf("father read :%s\n",buf);}}return 0;}



从图中可以验证管道的单向通信,如果想实现父进程给子进程法消息,我们该怎么办?
我们可以再新建一个管道,使父进程写,子进程读,就完美解决了这个问题。

但是使用管道还有四种特殊情况要注意(以父进程读,子进程写为例):
1.如果子进程的写端的文件描述符关闭了,此时管道还有数据,此时父进程在读取完管道剩余数据后,在此read会阻塞返回0,就像读到文件尾。

#include<stdio.h>#include<stdio.h>#include<unistd.h>#include<errno.h>#include<string.h>//创建管道,子进程只写,父进程只读int main(){int fds[2];int ret=pipe(fds);if(ret==-1){printf("create pipe errro!\n");return 1;}pid_t id=fork();if(id<0){printf("fork error!\n");return 2;}else if(id==0){//childclose (fds[0]);//关闭读int i=0;char* msg="I am child!";while(i<5){write(fds[1],msg,strlen(msg)+1);sleep(1);++i;}close(fds[1]);//写入5次,关闭写}else{//fatherclose(fds[1]);//关闭写char  buf[1024];int j=0;while(j<100){memset(buf,'\0',sizeof(buf));int ret=read(fds[0],buf,sizeof(buf));printf("%s; code is:%d\n",buf,ret);++j;}if(waitpid(id,NULL,0)<0)return 3;}return 0;}


2.如果子进程的写端没有关闭,但是也没有向管道写数据,此时父进程读取完管道的剩余数据,再次read会阻塞,直到管道中有数据读才返回。
#include<stdio.h>#include<stdio.h>#include<unistd.h>#include<errno.h>#include<sys/wait.h>#include<string.h>//创建管道,子进程只写,父进程只读int main(){int fds[2];int ret=pipe(fds);if(ret==-1){printf("create pipe errro!\n");return 1;}pid_t id=fork();if(id<0){printf("fork error!\n");return 2;}else if(id==0){//childclose (fds[0]);//关闭读int i=0;char* msg="I am child!";while(1){if(i<3){write(fds[1],msg,strlen(msg)+1);}sleep(1);}//close(fds[1]);//写入5次,关闭写}else{//fatherclose(fds[1]);//关闭写char  buf[1024];int j=0;while(j<20){memset(buf,'\0',sizeof(buf));int ret=read(fds[0],buf,sizeof(buf));printf("%s; code is:%d\n",buf,ret);++j;}if(waitpid(id,NULL,0)<0)return 3;}return 0;}


3.如果父进程的读端关闭了,这时子进程向管道写,那么进程会收到SIGPIPE,通常会导致程序异常终止。
#include<stdio.h>#include<stdio.h>#include<unistd.h>#include<errno.h>#include<sys/wait.h>#include<string.h>//创建管道,子进程只写,父进程只读int main(){int fds[2];int ret=pipe(fds);if(ret==-1){printf("create pipe errro!\n");return 1;}pid_t id=fork();if(id<0){printf("fork error!\n");return 2;}else if(id==0){//childclose (fds[0]);//关闭读int i=0;char* msg="I am child!";while(1){if(i<10){write(fds[1],msg,strlen(msg)+1);}sleep(1);++i;}//close(fds[1]);//写入5次,关闭写}else{//fatherclose(fds[1]);//关闭写char  buf[1024];int j=0;while(j<5){memset(buf,'\0',sizeof(buf));int ret=read(fds[0],buf,sizeof(buf));printf("%s; code is:%d\n",buf,ret);++j;}close(fds[0]);sleep(10);//睡眠10秒,终止if(waitpid(id,NULL,0)<0)return 3;}return 0;}


4.父进程的读端口没关闭,但是父进程也没有从管道里读文件,当子进程向管道写数据时,管道被写满时,再次write会阻塞,直到管道有空位置才写入数据并返回。
#include<stdio.h>#include<stdio.h>#include<unistd.h>#include<errno.h>#include<sys/wait.h>#include<string.h>//创建管道,子进程只写,父进程只读int main(){int fds[2];int ret=pipe(fds);if(ret==-1){printf("create pipe errro!\n");return 1;}pid_t id=fork();if(id<0){printf("fork error!\n");return 2;}else if(id==0){//childclose (fds[0]);//关闭读int i=0;char* msg="I am child!";while(1){write(fds[1],msg,strlen(msg)+1);printf("%s; code is:%d\n",msg,i);//sleep(1);++i;}}else{//fatherclose(fds[1]);//关闭写}return 0;}















0 0
原创粉丝点击