Linux C:管道的实现原理,命名管道

目录

一、管道

二、利用管道将写进程和都进程连接起来

三、命名管道


一、管道

在Unix/Linux 中 命令行

   cmd1  |  cmd2                #例如    history | grep sqlplus 

其中 cmd1  是  cmd2 是Linux中独立的程序 , 而 “ | ” 符号在Linux 称之为管道符。管道一端用来读另一端用来写。 把前一个进程的输出结果作为后一个进程的输入参数。例如

 history | grep sqlplus  命令,前面一个是展示所有的历史命令 ,而后面一个是对输入中过滤出待关键字的sqlplus 的字符串。 两个合起来就是  展示所有的带”sqlplus“的历史命令。

在C程序中,方法 int  flag  =  pipe(int pd[2]);  方法,输入参数是一个至少2个元素的int数组。用来创建两个文件描述符,这两个文件描述符链接同一个临时文件, pd[0]用于从管道中读数据, pd [1]用于从管道中写数据。 如果创建成功flag 值为0 否则为-1。.

注意:在创建pipe 后,其中的一个文件描述符 pd[0] 没有'w'权限,只有’r‘权限,而 pd[1] 没有'r'权限,只有’w‘权限。

对文件描述符和IO重定向不了解的可以看看:

https://blog.csdn.net/superSmart_Dong/article/details/118531682

在下例代码展示了用子进程和父进程利用管道读写数据的过程。由于子进程创建时会完全继承父进程的文件描述符。意味着父进程中存在pd[2],那么子进程也有一样的pd[2]。所以一个进程如果当作管道的写进程,那么它的读文件描述符就要先关闭,如果一个进程如果当作管道的读进程,那么它的写文件描述符就要关闭。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int pd[2] ,n,i;
char line[256];

int main(){
     pipe(pd);    //pd[1]用于写 , pd[0]用于读
     printf("pd[ %d , %d ]",pd[0],pd[1]);
     if(fork()){
         //parent分支
         printf("parent %d close pd[0]  \n",getpid());
         close(pd[0]);
         i =0;
         while( i++ <10){
              //假设父进程向管道写入10次;
              n = write(pd[1],"I AM YOUR PAPA",16);
              printf("parent %d write  %d byte to pipe\n",getpid(),n);
         }
        printf("parent %d exit\n",getpid());
     }else{
        //child 分支
        printf("child %d close pd[1]  \n",getpid());
        close(pd[1]);
        while( 1){
          //管道读入诺干;
          n = read(pd[0],line,128);
          if(n){
	        printf(" child %d read  %d byte from pipe\n :%s \n",getpid(),n,line);
          }else{
	        exit(0);
          }
          
        }
     }
     
}

输出结果可能不为一:其中一种输出结果如图。

二、利用管道将写进程和都进程连接起来

在Linux 中当 sh  获取命令行   cmd1 | cmd2   时,会复刻出一个子进程sh。子进程sh会观察命令行中是否有 ”|“ 管道符号,把命令行划分为头部和尾部

然后子进程执行一下代码段:从而实现两个进程通过管道和重定向IO实现对子进程的标准输入的内容时父进程的标准输出的内容。

int pd[2];
pipe(pd);
int pid =fork();
if(pid){
  close(pd[0]);       //父进程用于从管道中写入数据,所以关闭读fd(pd[0])
  close(1);           
  dup(pd[1]);         //拿pd[1]复制出一个最小未使用的fd = 1,让标准输出指向管道pd[1]所指的物理文件
  close(pd[1]);       //关闭原来的pd[1]
  exec(cmd1);
}else{
  close(pd[1]);       //子进程用于从管道中读入数据,所以关闭写fd(pd[1])
  close(0);           
  dup(pd[0]);         //拿pd[0]复制出一个最小未使用的fd =0,让标准输入指向管道pd[0]所指的物理文件
  close(pd[0]);       //关闭原来的pd[0]
  exec(cmd2);
}

三、命名管道

  命名管道又叫做FIFO,在C语言中可以为管道命名。一个被赋予名字的管道。可以实现在两个独立进程或者两个项目工程中进行数据交互。命名管道在文件系统中以特殊文件的形式存在,不是临时文件,意味着,如果没有把命名管道文件进行 rm 或者 unlink 删除,则该管道会一致存在下去,除非储存介质损坏。

在 sh 中:创建命名管道的命令为:

mknod  pipename  p

在C程序中,创建命名管道的系统调用为

int r =mknod("pipename",S_IFIFO,0)

可以利用在命名管道实现两个独立进程的数据交互

/*********写管道进程*****************/

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
char *line = "name pipe message";
int main(){
    int fd ;
    mknod("mypipe",I_SFIFO,0);
    fd = open("mypipe",O_WRONLY);
    write(fd,line,strlen(line));
    close(fd);
}



在写另外一个用于读管道的C程序

/*********读管道进程*****************/

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){
    char buf[128];
    int fd = open("mypipe",O_RDONLY);
    read(fd,buf,128);
    printf("%s\n",buf);
    close(fd);
}



对这两个C程序分别进行编译。如果管道没数据就读内容的话,得到的输入也是空的。就算进程生命结束,创建出来的命名管道依旧是存在的。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值