Linux基础篇十二——管道

来源:互联网 发布:mac word 窗口切换 编辑:程序博客网 时间:2024/06/02 15:20

管道是 Linux 支持的最初 Unix IPC 形式之一,我们可以使用管道使得两个进程之间进行通信。

管道通信实现的本质:控制同一文件在两个进程中仅打开读端或写端进行收发数据。

1.匿名管道

顾名思义,匿名管道是我们在进程里创建出来并且没有给其赋予指定文件名的一类管道,它可用于两个具有亲属关系的进程之间的通信。

在我们创建子进程时,子进程会创建自己的地址空间但是会复制父进程的文件描述符表,所以在子进程和父进程里我们使用同样的文件描述符指向的是同一个文件,并且,在我们打开文件时分别使用读方法和写方法打开我们找到的是同一个文件但是我们得到的描述符是不一样的。如图示



在上图里面,我们不难看出,管道文件被父子进程共享,这就说明了进程间通信的本质是共享数据。

匿名管道的特点:
·依赖于文件系统
·只能运用于有关系的进程eg:父进程 子进程之间
**进程打开文件不进行关闭,在进程退出时文件被自动关闭
·管道的生命周期和进程相同
·管道内部提供进程间同步与互斥机制
·单向数据通信
管道的特殊状态:
·管道的写端写满之后会进行停止等待,读端类似
·管道的读端关闭,但写端没有关闭并且持续写入,此时,写端会收到异常信号并关闭
·管道的写端关闭,但读端没有关闭并且持续读取,直到管道结尾并返回0.


匿名管道的创建:

使用#include <unistd.h> int pipe(int fd[2]) 函数创建匿名管道,给出一个简单的例子

#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<fcntl.h>#include<string.h>int main(){int pipe_fd[2];if(pipe(pipe_fd) < 0){perror("pipe");return 1;}pid_t id = fork();//childint i = 0;if(id == 0){close(pipe_fd[0]);const char*msg = "I am child pro...\n";int count = 5;while(1){write(pipe_fd[1],msg,strlen(msg));if(count -- == 0)break;printf("child write data :%d\n",++i);}//close(pipe_fd[1]);}//fatherelse{close(pipe_fd[1]);char buf[1024];int count = 5;while(count -- > 0){memset(buf,'\0',sizeof(buf));ssize_t _s = read(pipe_fd[0],buf,sizeof(buf)-1);if(_s >0)printf("client -> server: %s\n",buf);else if(_s == 0){printf("read pipe EOF...\n");break;}elsebreak;}//close(pipe_fd[0]);int status = 0;pid_t ret = waitpid(id,&status,0);if(ret == id){printf("wait success...\n");printf("get sig: %d",status&0xff);printf("exit code: %d",status >> 8&0xff);}}return 0;}
**我们使用status来接收程序退出的状态码,而它的低八位是退出信号,高八位是退出码

2.命名管道

与匿名管道不同,命名管道不仅可以用于具有亲属关系的进程之间的通信,还可以用于完全无关系的两个管道的通信。我们使一个进程负责创建管道文件另一个进程只需要打开该管道文件就可以在两个进程之间进行通信.

我们仍然使用一个例子来说明命名管道的通信:

client端程序

#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<string.h>#include<fcntl.h>int main(){int fd = open("./myfifo",O_WRONLY);if(fd < 0){perror("open error...");return 1;}char buf[1024];while(1){memset(buf,'\0',sizeof(buf));printf("Please enter# ");fflush(stdout);ssize_t _s = read(1,buf,sizeof(buf));if(_s > 0){buf[_s-1]='\0';write(fd,buf,strlen(buf));}}close(fd);return 0;}

server端程序:

#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<string.h>int main(){if(mkfifo("./myfifo",S_IFIFO | 0644) < 0){perror("mkfifo");return 1;}int fd = open("./myfifo",O_RDONLY);if( fd < 0 ){perror("open error...");return 2;}char buf[1024];while(1){memset(buf,'\0',sizeof(buf));ssize_t _s = read(fd,buf,sizeof(buf)-1);if(_s > 0 ){printf("Client->Server# %s\n",buf);}else{break;}}close(fd);return 0;}

我们让客户端进行数据的写入让服务端进行数据的读出,始终保持整个通信处于半双工的状态,并且,谁创建谁回收,我们让服务端自己回收关闭我们的管道文件。这样我们就完成了两个毫无关系的进程之间的通信。

一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

管道有容量,它的容量大小存储于一个叫做PIPE_BUF的字段里,它规定了管道缓冲区的大小。其位于 include/linux/limits.h中,我们可以自行查看。不同的内核版本可能会有所不同。Posix.1 要求 PIPE_BUF 至少为 512 字节,red hat 7.2 中为 4096。

管道的原理:管道实际上是在内核创建了一个inode和一个指向它的固定大小的内核缓冲区,读写的文件描述符不同但指向了同一块内核和缓冲区。管道pipe是一类特殊的文件pipefs,在磁盘里没有映像,只存在于内存中,这样设计的原因是处于对管道文件读写速度的优化。

如果我们需要实现全双工的通信方式就在两个进程之间创建两个管道,一个正向通信另一个反之。


0 0