Linux网络编程学习(六) ----- 管道(第四章)

时间:2022-05-19 02:56:09

1、管道的定义

管道就是将一个程序的输出和另外一个程序的输入连接起来的单向通道,比如命令: ls -l|more,就建立了一个管道,获取ls -l的输出作为more的输入,数据就沿着管道从管道的左边流到了管道的右边。

实际上内核为进程建立了两个句柄f1和f2,进程通过句柄f1向管道写入数据,同时通过f2从管道读出数据,这里是同一个进程建立了自己的管道

进程间的管道:通过fork出来的子进程,与父进程一样,拥有对同一个管道的读写句柄,但管道是单向的,因此要决定数据流向,如父进程到子进程或者子进程到父进程,那么不需要的句柄就要关闭

2、管道的建立和使用

1)通过pipe()函数建立管道,函数声明是int pipe(fd[2]) ,其中fd[0]用来从管道读,fd[1]向管道写

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = “Hello, world!\n”;
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror(“fork”);
exit(1);
}
if(childpid == 0)
{
/* 子进程关闭管道的读句柄 */
close(fd[0]);
/* 通过写句柄向管道写入信息 */
write(fd[1], string, strlen(string));
_exit(0);
}
else
{
/* 父进程关闭管道的写句柄 */
close(fd[1]);
/* 通过读句柄从管道读出信息 */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf(“Received string: %s”, readbuffer);
}
return(0);
}

  假定了数据流向是子进程到父进程,那么就由子进程写,父进程读,所以子进程要关闭读句柄,而父进程要关闭写句柄

2)使用popen()/pclose()函数,函数声明是FILE* popen(char * command, char *type)

popen()首先调用pipe()建立一个管道,然后用fork()函数建立一个子进程,运行一个shell环节,然后在这个环境中运行"command"参数指定的程序,数据管道流向由type控制,要么为"r"或者为"w",两者取其一,且type只取一个字符,比如"rw",那么只取"r",最后用pclose()关闭即可

#include <stdio.h>
#define MAXSTRS 5
int main(void)
{
int cntr;
FILE *pipe_fp;
char *strings[MAXSTRS] = { "roy", "zixia", "gouki","supper", "mmwan"};
/* 用popen 建立管道 */
if (( pipe_fp = popen("sort", "w")) == NULL)
{
perror("popen");
exit(1);
}
/* Processing loop */
for(cntr=0; cntr<MAXSTRS; cntr++)
{
fputs(strings[cntr], pipe_fp);
fputc('\n', pipe_fp);
}
/* 关闭管道 */
pclose(pipe_fp);
return(0);
}

  popen()可以节省源代码,同时还可以在"command"中使用任意合法的shell指令,包括重定向和管道,如下均为合法

popen("ls ~roy", "r");
popen("sort > /tmp/zixia", "w");
popen("sort | uniq | more", "w");

  如下例子为建立了两个管道,一个读管道,一个写管道

#include <stdio.h>
int main(void)
{
FILE *pipein_fp, *pipeout_fp;
char readbuf[80];
/* 用popen 建立一个通向"ls:的读管道*/
if (( pipein_fp = popen("ls", "r")) == NULL)
{
perror("popen");
exit(1);
}
/* 用popen 建立一个通向"sort"的写管道 */
if (( pipeout_fp = popen("sort", "w")) == NULL)
{
perror("popen");
exit(1);
}
/* 进程循环 */
while(fgets(readbuf, 80, pipein_fp))
{
fputs(readbuf, pipeout_fp);
} /* 关闭打开的管道 */
pclose(pipein_fp);
pclose(pipeout_fp);
return(0);
}

  

3、tips

1)pipe()调用必须在fork()前

2)及时关闭不需要的管道句柄

3)管道只能实现父子进程之间的通信,若两个进程没有fork()关系,必须考虑其他进程通信方式