shell中管道的原理




    管道是linux中一个很重要的命令行操作,因为linux的每个命令都以完成一个任务为目的,只有把些命令组合起来才能做复杂的工作,当然这种方式也提供了很大的灵活性。那么bash中的管道是通过什么实现的呢? 先看一个简单的管道的例子:

#include <unistd.h>

int fd[2];
char * argv[ ]={"ls","-l","/etc",(char *)0};
char * argv2[ ]={"wc","-l",(char *)0};
char * envp[ ]={"PATH=/bin:/usr/bin",(char *)0};
void run_ls()
{
     dup2(fd[1],1);
     close(fd[0]);
     close(fd[1]);
     execve("/bin/ls",argv,envp);
}
void run_wc()
{
      dup2(fd[0],0);
     close(fd[0]);
     close(fd[1]);
     execve("/usr/bin/wc",argv2,envp);
}

int main()
{
    pipe(fd);
    if(fork()==0)
        run_ls();
    else
        run_wc();
     return 0;
}

 
    这个程序的执行结果等效于ls|wc,从代码上看,主程序调用pipe来创建一个管道,如下图:


   对fd[1]进行写操作时会把数据写到内核中的pipe对应的buffer中,而对fd[0]进行读操作时,就是从pipe的buf中读取数据,这样就把fd[0]和fd[1]连接成一个"管道".
   继续看main函数中的代码,fork调用创建出子进程,子进程会继承父进程的文件描述符表,创建子进程后管道的状态是:

    两个进程中的fd[0],fd[1]的值是相同的,它们所指向的内核中的表示文件的结构体也是相的。先不看dup2调用,在子进程关闭fd[1],父进程关闭fd[0]后:

这样在子进程和父进程之间就建立起一个管道,子进程向fd[1]写的数据,父进程可以通过读fd[0]读出。再看dup2调用,父进程中又把标准输入对应的文件结构体复制成了fd[0]对应的,子进程中把标准输出对应的文件结构体复制成了fd[1]对应的,这个结构体中包含了对文件的操作函数。这样子进程中写0号文件描述符的数据实际就会写到内核中的pipe的buf中,而父进程从1这个文件描述符对应的文件读取数据,现在1已经不再指向标准输出,而是指向了pipe的buf。
   虽然这个例子很简单,但已经可以说明bash中管道的原理。从上面的分析我们可以看出,linux中创建进程是从父进程"fork"出来,然后再execve,而不是在创建时就指定它要运行的函数,完成独立地创建,这样天然的进程的继承关系,为管道的实现提供了很大的方便,因为管道的实现利用了子进程继承父进程的文件描述符表这一特性。
   关于内核中对pipe的支持,参见下一篇文章:linux内核中pipe的实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值