目录
管道
管道的概念
管道是一种最基本的IPC机制,也称匿名管道,应用于有血缘关系的进程之间,完成数据传递。调用pipe函数即可创建一个管道。
有如下特质:
-
管道的本质是一块内核缓冲区
-
由两个文件描述符引用,一个表示读端,一个表示写端。
-
规定数据从管道的写端流入管道,从读端流出。
-
当两个进程都终结的时候,管道也自动消失。
-
管道的读端和写端默认都是阻塞的。
管道的原理
-
管道的实质是内核缓冲区,内部使用环形队列实现。(缓刑管道效率高)
-
默认缓冲区大小为4K,可以使用ulimit -a命令获取大小。
-
实际操作过程中缓冲区会根据数据压力做适当调整。
管道的局限性
-
数据一旦被读走,便不在管道中存在,不可反复读取。
-
数据只能在一个方向上流动,若要实现双向流动,必须使用两个管道
-
只能在有血缘关系的进程间使用管道。
创建管道-pipe函数
函数作用:
创建一个管道
函数原型:
int pipe(int fd[2]);
函数参数:
若函数调用成功,fd[0]存放管道的读端,fd[1]存放管道的写端
返回值:
成功返回0;
失败返回-1,并设置errno值。
函数调用成功返回读端和写端的文件描述符,其中*fd[0]是读端, fd[1]是写端*,*向管道读写数据是通过使用这两个文件描述符进行的,读写管道的实质是操作内核缓冲区。*
管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?
父子进程使用管道通信
一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在血缘关系,这里的血缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。*父子进程间具有相同的文件描述符,且指向同一个管道pipe*,其他没有关系的进程不能获得pipe()产生的两个文件描述符,也就不能利用同一个管道进行通信。
*第一步:父进程创建管道*
*第二步:父进程fork出子进程*
*第三步:父进程关闭fd[0],子进程关闭fd[1]*
*创建步骤总结:*
-
父进程调用pipe函数创建管道,得到两个文件描述符fd[0]和fd[1],分别指向管道的读端和写端。
-
父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管。
-
父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出,这样就实现了父子进程间通信。
管道练习
使用管道完成父子进程间通信?
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> int main() { int fd[2]; int ret = pipe(fd); if(ret<0) { perror("pipe error"); return -1; } //创建子进程 pid_t pid = fork(); if(pid<0) { perror("fork error"); return -1; } else if(pid>0) { //关闭读端 close(fd[0]); write(fd[1],"hello world",strlen("hello world")); wait(NULL); } else { //关闭写段 close(fd[1]); char buf[64]; memset(buf,0x00,sizeof(buf)); int n = read(fd[0],buf,sizeof(buf)); printf("read over, n==[%d],buf==[%s]\n",n,buf); } return 0; }
管道的读写行为
-
读操作
有数据
read正常读,返回读出的字节数
无数据
写端全部关闭
read解除阻塞,返回0, 相当于读文件读到了尾部
没有全部关闭
read阻塞
-
写操作
读端全部关闭
管道破裂,进程终止, 内核给当前进程发SIGPIPE信号
读端没全部关闭
缓冲区写满了
write阻塞
缓冲区没有满
继续write
如何设置管道为非阻塞
默认情况下,管道的读写两端都是阻塞的,若要设置读或者写端为非阻塞,则可参
考下列三个步骤进行:
第1步: int flags = fcntl(fd[0], F_GETFL, 0);
第2步: flag |= O_NONBLOCK;
第3步: fcntl(fd[0], F_SETFL, flags);
若是读端设置为非阻塞:
Ø 写端没有关闭,管道中没有数据可读,则read返回-1;
Ø 写端没有关闭,管道中有数据可读,则read返回实际读到的字节数
Ø 写端已经关闭,管道中有数据可读,则read返回实际读到的字节数
Ø 写端已经关闭,管道中没有数据可读,则read返回0
如何查看管道缓冲区大小
-
命令
ulimit -a
-
函数
long fpathconf(int fd, int name);
printf("pipe size==[%ld]\n", fpathconf(fd[0], _PC_PIPE_BUF));
printf("pipe size==[%ld]\n", fpathconf(fd[1], _PC_PIPE_BUF));