管道
PIPE:半双工通信(无名管道),数据在一个方向上流动(父子进程间通信)。常用在终端敲入一个命令,让shell执行则创建一个进程,通道将敲入的前一个命令的标准输出连接到后一条命令的标准输入。
Int pipe(int fd[2]);//创建管道. fd[0]是读管道文件描述符,fd[1]是写管道文件描述符。
半双工管道的方法:
1、在单进程中,管道两端相互连接。
2、在单进程中,管道fd[1]数据经过内核,fd[0]从内核输出.
3、在父子进程中,父进程先pipe,再fork(如图1),然后数据从父进程fd[0]进,从子进程fd[1]出(如图2)。(该方式关闭父的fd[1],子的fd[0])
4、利用方法3的将管道文件描述符复制到标准输入或标准输出.子进程执行另一个程序(该程序从标准输入、输出中读、写).
5、利用管道实现父子进程之间的同步。(编写TELL_WAIT, TELL_PARENT, TELL_CHILD等等)
6、利用管道实现的标准I/O popen,pclose函数及用popen实现过滤器功能.
7、协调进程.
方法3表述如图1,图2:
注:
A:在3方法中 read和write需注意:
a. read一个关闭写端的管道,数据全部read后,返回0.表示文件结束(若写端还有进程(这里指的是写进程吗???),则不会文件结束)
b. Write一个关闭读端的管道,将会产生SIGPIPE信号,忽略或从捕捉函数中返回,write返回-1,error是EPIPE.
B: PIPE_BUF 规定了内核缓冲区的大小,在多进程同时对同一个管道write,并且数据超过PIPE_BUF则各进程所写数据会产生交
互.用pathconf或fpathconf函数可以确定PIPE_BUF值.
方法4表述应用:将文件复制到分页程序(通过管道将输出直接送到分页程序)
#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)
{//父进程
close(fd[0]); /* close read end */
while (fgets(line, MAXLINE, fp) != NULL) /* parent copies argv[1] to pipe */
{
n = strlen(line);
if (write(fd[1], line, n) != n) err_sys("write error to pipe"); //从命令行文件中read数write到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
{ //子进程
close(fd[1]); /* close write end */
if (fd[0] != STDIN_FILENO)//判断是否等于标准输入
{ //这里dup2前要先判断,避免fd[0]的副本STDIN_FILENO在调用close(fd[0])时被关闭.
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) err_sys("dup2 error to stdin");//将fd[0]复制给标准输入.(此后标准输入的数据会到PIPE的读端)
close(fd[0]); /* don't need this after dup2 */
}
if ((pager = getenv("PAGER")) == NULL) pager = DEF_PAGER; /*从环境变量中或者直接赋值get分页程序*/
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);
}
在APUE中执行:
执行过程:
子进程负责获取标准输入agrv[1]文件(文件供给分页程序作为参数)并执行分页程序的结果读到管道;
父进程负责获取agrv[1]文件中内容并写到管道(分页程序);
将子进程中dup2操作注释到后执行:
........
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 */
}
........
执行结果:
若将父进程中的write注释掉,执行则无任何打印就退出.