inux的shell中有个很常用的东西管道,很多人认为他是shell的精华了 (我没什么感觉,可能是用的比较少的原因),意思就是把一个命令的输出,作为另一个进程的输入。而在上操作系统课程时候,老师曾经给我们讲过进程通信中的 管道这一个概念。在linux具体的实现就是pipe函数,可以说这个和shell中的管道,我认为就是一个东西,shell中的管道就是用pipe函数 来实现的(有点武断,因为我没看过 shell的源代码)。为了说明这一点,我们写一个程序来模拟管道。
具体例子:我们输入 ls -l | wc这是一个管道的简单例子,大家可以自己试试,可以通过man帮助来查查这个指令的意思,应该不难。
前提知识介绍:
1.文件的数据结构描述:管道通信,就是在2个进程间建立一个管道(其实具体是的实现,是一个虚拟的文件,这个文件只存在于内存中,所以是个虚拟 的)一个进程掌握写入段,另一个掌握读出段。linux把设备看作文件,以前我们就说过标准输入是0,输出是1,错误输出是2。具体的意思是:每个进程都 有一个files数组,files[0]就是键盘了(一般是,我们程序就不是了,我们改掉了), files[1]就是显示器, files[2]是错误输出(应该是null)。
我们的思路是:由父进程创建2个子进程b 和c,父进程传建一个管道,管道是个虚拟文件,我们把这个文件输出段,其实也是一个file结构替换掉子进程的标准输入文件,输入段替换掉子进程的标准输入文件,这样我们就是先了shell中的管道。
具体代码:
[root@liumengli pipe]# cat pipe_father_3.c
#include "stdio.h"
#include "unistd.h"
int main() {
pid_t child_b, child_c;
int pipefds[2]; //[0] for read, [1] for write
char * arg1[] = {"/usr/bin/wc", NULL};
char * arg2[] = {"/bin/ls", "-l", NULL};
//父进程
pipe(pipefds); // 开始创建管道的2段
if(!(child_b = fork())) { //创建子进程b
close(pipefds[1]); /* 关掉管道的读取端*/
close(0); //关掉b进程的标准输入文件,其实就是键盘
dup2(pipefds[0], 0); //将我们管道的输出段复制到 b进程的标准输入中
close(pipefds[0]); //关掉输出
execve("/usr/bin/wc", arg1, NULL); //执行wc进程
printf("pid is %d, wc/n", getpid()); //只有出错才会到这里
}
close(pipefds[0]); /*close read*/
if(!(child_c = fork())) { //创建子进程c
close(1); //和上面雷同
dup2(pipefds[1], 1);
close(pipefds[1]);
execve("/bin/ls", arg2, NULL);
printf("pid is %d, ls/n", getpid());
}
close(pipefds[1]);
wait4(child_b, NULL, 0, NULL);
return 0;
}
[root@liumengli pipe]# ls -l | wc
15 128 763
[root@liumengli pipe]# ./pipe_father_3
14 128 763