管道是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進程間通信-管道深入理解 (個人感覺在進程間通信方面,總結的不錯,值得參考)