管道是UNIX 系统中比较古老的一种,他有两个比较大的局限性(缺点)。
(1)历史上的通道都是半双工的(即数据只能往一个方向流)。某些系统是全双工的,但是由于大多数系统都是半双工的,为了提高可移植性,应该将通道视作半双工去使用。
(2)管道的只能使用在有公共祖先的进程之间。一般情况下,一个进程由进程创建,在调用fork之后,这个管道就能在父子进程之间使用了。
虽然有以上的两大缺点,但是管道依然是UNIX中最常用的一个命令。在shell命令中的 “ | ”,就是一个管道。每当使用“ | ”时,shell 会为其命一条命令创建一个进程,然后将管道前一条命令的标准输出与后一条命令的标准输入相连接。
shell中管道的具体使用
1、管道的创建
fd[0]为读而打开,fd[1] 为写而打开。fd[1]的输出是fd[0]的输入。
2、管道的通信
(1)父进程创建管道,得到两个⽂件描述符指向管道的两端
(2)父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。
(3)父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管道写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。
3、书中代码的实现,实现的父进程写,子进程读的一个管道实现。
#include "apue.h"
int
main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];if (pipe(fd) < 0)
err_sys("pipe error");
if ((pid = fork()) < 0) {
err_sys("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);
}
4、管道读数据会有四种情况
(1)读端不读,写端一直写
(2)写端不写,读端一直读
(3)读端一直读,写端关闭
(4)写端一直写,读端关闭
5、书中介绍了一个实例,将一个文件的内容,分页显示出来,代码如下:
#include "apue.h"
#include <sys/wait.h>#define DEF_PAGER "/bin/more" /* default pager program */
int
main(int argc, char *argv[])
{
int n;
int fd[2];
pid_t pid;
char *pager, *argv0;
char line[MAXLINE];
FILE *fp;if (argc != 2)
err_quit("usage: a.out <pathname>");if ((fp = fopen(argv[1], "r")) == NULL)
err_sys("can't open %s", argv[1]);
if (pipe(fd) < 0)
err_sys("pipe error");if ((pid = fork()) < 0) {
err_sys("fork error");
}
else if (pid > 0)
{ /* parent */
close(fd[0]); /* close read end *//* parent copies argv[1] to pipe */
while (fgets(line, MAXLINE, fp) != NULL)
{
n = strlen(line);
if (write(fd[1], line, n) != n)
err_sys("write error to pipe");
}
if (ferror(fp))
err_sys("fgets error");close(fd[1]); /* close write end of pipe for reader */
if (waitpid(pid, NULL, 0) < 0)
err_sys("waitpid error");
exit(0);
}
else
{ /* child */
close(fd[1]); /* close write end */
if (fd[0] != STDIN_FILENO) {
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
err_sys("dup2 error to stdin");
close(fd[0]); /* don't need this after dup2 */
}/* get arguments for execl() */
if ((pager = getenv("PAGER")) == NULL)
pager = DEF_PAGER;
if ((argv0 = strrchr(pager, '/')) != NULL)
argv0++; /* step past rightmost slash */
else
argv0 = pager; /* no slash in pager */if (execl(pager, argv0, (char *)0) < 0)
err_sys("execl error for %s", pager);
}
exit(0);
}
部分参考:Linux进程间通信-管道深入理解 (个人感觉在进程间通信方面,总结的不错,值得参考)