1.管道的特点
(1)无论有名还是无名,写入管道的数据都在内存中。
(2)管道是一种半双工通信方式(通信方式有单工(固定接收方发送方,接收方只能接收,发送方只能发送)、半双工(如同对讲机,两端可以切换写入和接收)、全双工(每端都可以接收和写入,且可以同时进行))。
(3)有名和无名管道的区别:有名可以在任意进程间使用,而无名只能在父子进程间。
(4)对于管道,我们只能以只读打开或者只写打开。
(5)有名管道得需要读和写都打开,否则只以读打开或者只以写打开都会阻塞。
(6)管道中没有数据时,读管道read会发生阻塞,直至管道被写入数据,读出数据,再次阻塞。如果写端被关闭,读端read返回值会成为0。如果读端被关闭,写端向管道写入数据后会自动退出。
(7)管道写满后,写端会阻塞。
(8)由于管道的数据都在内存中,所以查看管道大小总是0。
管道的实现:
写入从头指针开始,读取从尾指针开始。写入之后,头指针挪动,读取之后尾指针挪动。如果是头指针赶上尾指针,那么管道被写满,写就会被阻塞。如果是尾指针赶上头指针,那么管道为空,read阻塞。
管道可以用来在两个进程之间传递数据,如: ps -ef | grep “bash”, 其中‘|’就是管道,其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行过滤。
管道大小是可以查看的,如下图可以看到管道512字节为一个单位,乘以8就是整个管道大小。
2.有名管道
2.1有名管道的创建和头文件
有名管道可以在任意两个进程之间通信
有名管道的创建:
(1)命令创建: mkfifo + 管道名
(2)系统调用创建
#include <sys/types.h>#include <sys/stat.h>//filename 是管道名 mode 是创建的文件访问权限int mkfifo( const char *filename, mode_t mode);
2.2有名管道的使用
进程 a 从键盘获取的数据循环传递给另一个进程 b。
我们可以用创建一个文件,a从键盘获取字符串存放到文件中,b从文件中读取。但是文件是存在磁盘上的,速度没有管道快,而且从文件读不到数据不会等待,但是管道不一样,b从管道读不到数据会阻塞,直到a又向管道中写入数据。
首先用命令mkfifo+管道名称创建管道
写端代码:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<assert.h>#include<fcntl.h>#include<sys/stat.h>int main(){int fd = open("fifo",O_WRONLY);assert(fd != -1);printf("open FIFO success\n");while(1){printf("please input:\n");char buff[128] = {0};fgets(buff,128,stdin);write(fd,buff,strlen(buff)-1);if(strncmp(buff,"end",3)==0){break;}}close(fd);exit(0);}
读端代码:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<assert.h>#include<fcntl.h>#include<sys/stat.h>int main(){int fd = open("fifo",O_RDONLY);assert(fd != -1);printf("FIFO open success\n");while(1){char buff[128] = {0};int n = read(fd,buff,127);if(n <= 0 || (strncmp(buff, "end", 3)==0)){break;}printf("%s\n",buff);}close(fd);exit(0);}
(1)如果读端或者写端先打开,那么将会在open阻塞住 ,直至另一端也打开管道。
(2)写端和读端都打开后,读端会在read处阻塞,写端向管道中写入数据后,读端才正常执行read,执行完继续在read阻塞。
(3)如果读端关闭,写端在向管道输入数据后会触发信号(SIGPIPE)自动退出。
(4)如果写端关闭,读端read函数值会返回0。
当写端和读端都在打开着,管道中的数据如果没有及时读出,不会消失。比如将读端加上休眠10s,代码如下:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<assert.h>#include<fcntl.h>#include<sys/stat.h>int main(){int fd = open("fifo",O_RDONLY);assert(fd != -1);printf("FIFO open success\n");while(1){char buff[128] = {0};int n = read(fd,buff,127);if(n <= 0 || (strncmp(buff, "end", 3)==0)){break;}printf("%s\n",buff);sleep(10);}close(fd);exit(0);}
写端和读端执行情况:
发现,在读端还没来得及读走管道数据的时候写端再次向管道写入数据,管道中没读走的数据会被读端下一次读走。
3.无名管道
无名管道主要应用于父子进程间的通信。
无名管道的创建:
#include <unistd.h>2. /*3. pipe()成功返回 0,失败返回-14. fds[0]是管道读端的描述符5. fds[1]是管道写端的描述符6. */7. int pipe( int fds[2]);
代码演示:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<assert.h>int main(){int fd[2];int res = pipe(fd);assert(res != -1);pid_t pid = fork();assert(pid != -1);if(pid == 0){close(fd[1]);char buff[128] = {0};read(fd[0],buff,127);printf("child read:%s\n",buff);close(fd[0]);}else{close(fd[0]);char buff[128] = {0};printf("please input(less 128):\n");fgets(buff,128,stdin);write(fd[1],buff,strlen(buff)-1);close(fd[1]);}exit(0);}
执行结果: