管道是Unix系统IPC的最古老的形式,所有的Unix都支持管道,管道的局限性:
(1)半双工的,某些系统提供全双工,但是为了最佳可移植性,还是认为半双工
(2)只能在具有公共祖先的两个进程之间使用
管道通过调用pipe函数创建:
#include <unistd.h> init pipe(int fd[2]);
由参数fd返回两个文件描述符:f[0]为读打开,f[1]为写打开,f[1]的输出是f[0]的输入
单个进程中的管道几乎没有任何用处,通常进程会先调用pipe,接着调用fork,从而创建从父进程到子进程的IPC通道,反之亦然
对于fork之后数据流的方向取决于父进程与子进程关闭的文件描述符
当管道的一端被关闭后,有两条规则:
(1)当read一个写端已被关闭的管道时,在所有数据被读取后,read返回0,表示文件结束
(2)当write一个读端已被关闭的管道时,则产生信号SIGPIPE,如果忽略信号或者捕获该信号并从其处理程序返回,则write返回-1,errno设置为EPIPE
在写管道的时候,常量PIPE_BUF规定了内核的管道缓冲区大小
下面一个简单的程序实现了父进程到子进程的管道,父进程经管道向子进程传送数据
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <string.h> #include <sys/stat.h> #include <stropts.h> #define MAXLINE 4096 //输出错误信息并退出 void err_sys(const char *str) { fprintf(stderr, "%s\n", str); exit(1); } int main(void) { int n; int fd[2]; pid_t pid; char line[MAXLINE]; char *s = "hello wuyudong!\n"; if (pipe(fd) < 0) err_sys("pipe error"); if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid > 0) { /* 父进程 */ close(fd[0]); write(fd[1], s, strlen(s)); } else { /* 子进程 */ close(fd[1]); n = read(fd[0], line, MAXLINE); write(STDOUT_FILENO, line, n); } exit(0); }
wu@ubuntu:~/myapue$ gcc usepipe.c -o usepipe
wu@ubuntu:~/myapue$ ./usepipe
wu@ubuntu:~/myapue$ hello wuyudong!
上面实现的是半双工单向的管道,即从父进程写到管道,再到子进程从管道中读,最后再显示
接下来通过增加一个管道来实现一个双向数据流,具体步骤如下:
(1)创建2个管道(fd1和fd2)
(2)fork
(3)父进程关闭fd1[0]
(4)父进程关闭fd2[1]
(5)子进程关闭fd1[1]
(6)父进程关闭fd2[0]
具体代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <string.h> #include <sys/stat.h> #include <stropts.h> #define MAXLINE 4096 void client(int readfd, int writefd) { size_t len; ssize_t n; char buf[MAXLINE]; fgets(buf, MAXLINE, stdin); //输入文件路径保存在buf中 len = strlen(buf); if(buf[len-1] == '\n') //除去换行符 len--; write(writefd, buf, len); //将文件路径写入管道 while((n = read(readfd, buf, MAXLINE)) > 0) //从管道中读取n字符到buf write(STDOUT_FILENO, buf, n); //将buf中的n个字符显示终端 } void server(int readfd, int writefd) { int fd; ssize_t n; char buf[MAXLINE + 1]; //read函数:当管道或者FIFO包含的字节少于指定的字节数,read返回可用的字节数 if((n = read(readfd, buf, MAXLINE)) == 0) { printf("read end of file!"); exit(0); } buf[n] = '\0'; if((fd = open(buf, O_RDONLY))< 0) { snprintf(buf+n, sizeof(buf)-n,": can't open, %s\n", strerror(errno)); n = strlen(buf); write(writefd, buf, n); //将buf中的n个字节写入管道 } else { while((n = read(fd, buf, MAXLINE)) > 0) write(writefd, buf, n); close(fd); } } int main(int argc, char **argv) { int pipe1[2], pipe2[2]; pid_t childpid; //创建两个管道 pipe(pipe1); pipe(pipe2); if((childpid = fork()) == 0) { //子进程 close(pipe1[1]); //关闭pipe1的写 close(pipe2[0]); //关闭pipe2的读 server(pipe1[0], pipe2[1]); exit(0); } close(pipe1[0]); close(pipe2[1]); client(pipe2[0], pipe1[1]); waitpid(childpid, NULL, 0); exit(0); }
编译运行:
wu@ubuntu:~/myapue$ gcc usepipe1.c -o usepipe1
wu@ubuntu:~/myapue$ ./usepipe1
pipetest
hello wuyudong
pipe test pipe
wu@ubuntu:~/myapue$ ./usepipe1
/etc/shadow
/etc/shadow: can’t open, Permission denied
wu@ubuntu:~/myapue$ /no/test
bash: /no/test: No such file or directory
Comments