管道
Linux管道(pipe)提供一种单向(半双工)的进程间通讯机制。管道有一个读端和一个写端。从写端写入的数据可以重读端读出来。函数pipe()创建一个管道,返回两个文件描述符,fd[0]是读端,f[1]是写端。如下图
管道的读写可以使用Linux标准IO操作接口进行,例如read、write等。从图1不难看出,数据一直缓存在内核中直到被读取出来。
单个进程的管道几乎没有任何用处。通常调用pipe的进程接着调用fork,这样就创建了从父进程到子进程的IPC通道。如下图
缺省情况下,管道工作在阻塞方式下。进程读取管道时,如果管道为空,则读操作一直阻塞直到数据到达;进程写管道时,如果管道已满,写操作会阻塞进程直到足够数据被读出。
可以使用fcntl(fd, F_SETFL, O_NONBLOCK)设置管道工作为非阻塞IO方式。管道为空时,read接口会立即返回-1,errno设置为EAGAIN;管道满时,write接口会立即返回-1;errno设置为EAGAIN。
如果所有写端关闭,读端read操作会返回0(EOF)。如果所有读端关闭,write操作会给调用进程发送SIGPIPE信号,如果调用进程忽略SIGPIPE信号,则write操作会返回失败,并设置errno为EPIPE。使用pipe和fork的进程需要正确使用close关闭多于的fd,以确保EOF和SIGPIPE/EPIPE传递正确。
其他注意事项:
1、管道是字节流通信,没有消息边界的概念;
2、管道只能在具有共同祖先的进程间使用;
3、不能对管道进行lseek操作;
实际使用中,还有两个概念对理解管道至关重要。一个是管道容量。另一个是管道操作原子性。
管道容量有限。如果管道满,阻塞方式下write操作会阻塞,非阻塞方式下会返回失败。不同的系统有不同的管道容量限制。应用模块不应该依赖特定的容量限制,正确的设计是:一旦数据到达进程应尽快消费数据,避免写进程长时间阻塞。从linux 2.6.11版本开始,管道容量是65536字节。
POSIX 1-2001规定向管道写小于PIPE_BUF字节长度的数据时原子操作;写超过PIPE_BUF字节长度的数据不是原子操作。Linux上PIPE_BUF是4096字节,更细致的描述:
1、阻塞方式,n<=PIPE_BUF(n为写入的字节数,下同):写操作是原子操作,如果pipe空间不足则阻塞。
2、非阻塞方式,n<=PIPE_BUF:写操作是原子操作,如果pipe空间不足,则失败,errno设置为EAGAIN。
3、阻塞方式,n>PIPE_BUF:写操作不是原子操作,写入的数据可能与其他进程写入的交叉排列,写操作阻塞直到所有数据写完。
4、非阻塞方式,n>PIPE_BUF:写操作不是原子操作,如果pipe空间不足,则失败,errno设置为EAGAIN。写入的数据可能与其他进程写入的数据交叉排列。同时实际写入可能小于n(部分写入);调用者应该检查write实际写入的长度。
命名管道
FIFO又称命名管道。它使用mkfifo创建,使用open打开。只要有权限任何进程都可以打开一个管道,读端使用O_RDONLY标准打开FIFO,写端使用O_WRONLY标准打开FIFO。
FIFO与管道的唯一差别是创建和打开方式不同,在它们之上的IO操作时完全相同。一个给定的FIFO有多个写进程是很常见的。这就意味着如果不希望多进程所写的的数据互相穿插,则需要考虑原子写操作(如管道相同)。
应用场景
管道的有点是编程简单易用。
管道也存在一些不足:
1、需要通过内核传递数据
2、管道容量(64k)和PIPE_BUF不能修改
3、管道传递的是流。同步方式下编程比较复杂
因此管道适用于那些数据量少,性能要求不高的场合。
举例:
管道
#include <stdio.h> #include <unistd.h> int main(void) { int n; int fd[2]; pid_t pid; char line[MAXLINE]; if (pipe(fd) < 0) printf("pipe error"); if ((pid = fork()) < 0) { printf("fork error"); } else if (pid > 0) { /* parent */ close(fd[0]); write(fd[1], "hello world\n", 12); } else { /* child */ close(fd[1]); n = read(fd[0], line, MAXLINE); write(STDOUT_FILENO, line, n); } exit(0); }
命名管道
读端 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #define FIFO_NAME "myfifo" #define BUF_SIZE 1024 int main(void) { int fd; char buf[BUF_SIZE]; umask(0); //设置允许当前进程创建文件或者目录最大可操作的权限 fd = open(FIFO_NAME,O_RDONLY); read(fd,buf,BUF_SIZE); printf("Read content: %s\n",buf); close(fd); return 0; } 写端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #define FIFO_NAME "myfifo" #define BUF_SIZE 1024 int main(void) { int fd; char buf[BUF_SIZE] = "Hello Reader,I come from process named Writer!"; umask(0); //设置允许当前进程创建文件或者目录最大可操作的权限 if (mkfifo(FIFO_NAME,S_IFIFO|0666) == -1) { perror("mkfifo error!"); exit(1); } if ((fd = open(FIFO_NAME,O_WRONLY)) == -1) { perror("fopen error!"); exit(1); } write(fd,buf,strlen(buf)+1); close(fd); return 0; }