找回密码
 立即注册
Qt开源社区 门户 查看内容

Linux管道通信实践

2019-10-12 17:17| 发布者: admin| 查看: 1347| 评论: 0

摘要: 在进程间通信中管道是最古老的通信方式。它的通信方式是单向、先进先出、无结构、固定大小的字节流。因为管道传递数据的单向性,又称管道为半双工管道。从本质上说,管道是文件,用户进程在使用管道时就像使用文件一 ...
在进程间通信中管道是最古老的通信方式。它的通信方式是单向、先进先出、无结构、固定大小的字节流。因为管道传递数据的单向性,又称管道为半双工管道。从本质上说,管道是文件,用户进程在使用管道时就像使用文件一样。

管道分为无名管道和有名管道两种。无名管道只能用于父子进程或者兄弟进程(具有亲缘关系的进程)之间的通信,有名管道用于运行于同一台机器上的任意两个进程间的通信。

无名管道由pipe()函数创建。pipe()函数返回两个文件描述符,一个文件描述符用于读管道,另一个文件描述符用于写管道。一个进程通过write()将数据写入管道,另一个进程通过read()将数据读出。当一个进程创建一个子进程时,子进程继承了父进程已打开的管道。只有相关的进程,即发生pipe()调用的进程及其子进程才能共享管道。实质上无名管道是一块缓冲区(内存),内核必须对管道的操作进行同步,为此,内核使用了锁、等待队列和信号。

使用管道时,一个进程的输出可成为另一个进程的输入。当写进程利用write()向管道中写入数据时,系统根据库函数传递的文件描述符,可找到写入缓冲区的地址。在向缓冲区写入数据之前,检查缓冲区是否有足够的空间可容纳所有要写入的数据,以及缓冲区是否被读管道操作锁定。如果缓冲区有足够的空间且没有锁定,写入函数才锁定缓冲区,然后从写进程的地址空间中复制数据到缓冲区。否则,写入进程到相应的等待队列中等待直到符合条件被唤醒。当数据写入缓冲区之后,缓冲区被解锁,如果有读进程在等待读数据时需要唤醒读进程。

管道的读过程和写过程类似。但是,读管道进程可以在没有数据或缓冲区被锁定时立即返回错误信息,而不被阻塞,也可以在读管道端的等待队列中等待写进程写入数据,这依赖于管道的打开模式。当所有的进程完成了管道操作之后,可以关闭管道,共享缓冲区同时被释放。

有名管道也称命名管道或FIFO文件,即给管道取一个文件名,允许一组进程使用该文件名对管道进程共享。当一个进程使用有名管道时,管道是系统范围内的资源,可被任何进程使用。在程序中使用mkfifo()函数创建有名管道,有名管道创建好后,其他进程可以打开该有名管道,进行读管道或写管道操作。

注意:从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

1. 无名管道实践
利用pipe()创建一个或两个无名管道后,应用fork()创建一个子进程,实现父子进程之间的单向或双向通信。(1) 利用管道实现单向通信C源程序清单
/* Filename: unidirection.c     */

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#define MAXIMUM  80

int main()

{

         int fd[2];

         pid_t pid;

         charline[MAXIMUM];   

         if(pipe(fd)<0) {

                   printf("Failedto create the pipe. \n");

                   exit(1);

         }

         if((pid=fork()) < 0) {

                   printf("Failedto create the child process.\n");

                   close(fd[0]);

                   close(fd[1]);

                   exit(2);

         }

         if(pid>0) {                 

                   write(fd[1],"Howare you?\n",15);

                   printf("Parent:Successfully!\n");

                   sleep(1);         

         }else{

                   read(fd[0],line,MAXIMUM);

                   printf("Child:Read from the pipe:%s",line);

         }

         close(fd[0]);

         close(fd[1]);

         return 0;

}

/* 编译命令 gcc -o unidirection unidirection.c   */

/* 运行命令 ./ unidirection                                  */



思考: 1. 如果父进程往管道中写入信息后,马上读管道,会怎样呢?

   2. 如果父进程往管道写入信息后,睡眠1秒然后读管道;子进程读管道后马上往管道写信息会怎样呢?

(2) 利用管道实现双向通信的C源程序清单

/* Filename: twoway.c   */

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#define MAXLINE  80

int main()

{

         int fd1[2],fd2[2],n;

         pid_t pid;

         charline[MAXLINE];       

         if(pipe(fd1)<0) {

                   printf("Failedto create the pipe1. \n");

                   exit(0);

         }

         if(pipe(fd2)<0) {

                   printf("Failedto create the pipe2.  \n");

                   close(fd1[0]);

                   close(fd1[1]);

                   exit(0);

         }

         pid=fork();       

         if(pid>0) {

                   close(fd1[0]);

                   close(fd2[1]);

                   write(fd1[1],"Howare you?\n",15);

                   printf("Parent:Successfully!\n");

                   read(fd2[0],line, MAXLINE);

                   printf("Parent:Read from the pipe: %s",line);

                   close(fd1[1]);

                   close(fd2[0]);

                   sleep(1);

     }

         if (pid==0){

                   close(fd1[1]);

                   close(fd2[0]);

                   read(fd1[0],line,MAXLINE);

                   printf("Child:Read from the pipe:%s",line); 

                   write(fd2[1],"I'm fine, and you? \n", 30);

                   printf("Child:Successfully!\n");                

                   sleep(1);

                   close(fd1[0]);

                   close(fd2[1]);

sleep(1);

         }

         if(pid<0) {

                   printf("Failedto create the child process.\n");

                   close(fd1[0]);

                   close(fd1[1]);

                   close(fd2[0]);

                   close(fd2[1]);

         }

         return 0;

}

/* 编译命令 gcc -o twoway twoway-2.c    */

/* 执行程序 ./twoway                                  */



2. 命名管道实践

命名管道不同于无名管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。即命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。命名管道是一个设备文件,当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道。只要可以访问该路径的进程,就可以通过FIFO相互通信。实质上,命名管道是通过网络来完成进程之间的通信的,命名管道依赖于底层网络接口,其中包括有 DNS 服务,TCP/IP 协议等等机制,但是其屏蔽了底层的网络协议细节。需要注意的一点是:FIFO管道是由内核管理的,在进行通信时不与硬盘打交道,也就是说,当两个进程建立管道后,我们可以删除该命名管道文件而不影响这两个进程的通信。即通过有名管道建立两个进程之间的联系,实际通信的实现与无名管道类似,也是借助与内存缓冲区实现有名管道的写与读。下面的3个程序分别是创建管道、写管道和读管道。
/* Filename: createnamepipe.c     */

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

int main()

{

       mkfifo("my.p",0644);

        return 0;

}

/* Filename: write.c        */

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

int main()

{

    intfd=open("my.p",O_WRONLY);

    int i=0;

    if(fd==-1) {

       perror("open failure");

        exit(1);

     }

    while(1) {

       write(fd,&i,sizeof(i));

       printf("write %d is ok\n",i);

        i++;

        sleep(1);

    }

    return 0;

}

/* Filename: read.c         */

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

int main()

{

    int i=0;

    intfd=open("my.p",O_RDONLY);

    if(fd==-1) {

       perror("open failure");

        exit(1);

    }

    while(1) {

       read(fd,&i,sizeof(i));

       printf("%d\n",i);

        sleep(1);

    }

    return 0;

}

分别编辑、编译,如下图所示操作。



运行create创建命名管道。



在一个终端中运行write

在另一个终端中运行read

等两个进行建立通信后将管道文件删除后,两个write与read仍在通信中。



思考:下列命令使用了管道操作:

command1 | command2 | command3

command1命令的输出作为command2命令的输入,command2命令的输出作为command3命令的输入。请思考这里的管道是如何实现的。

注:操作符”|”只能处理经由前面一个指令传出的正确输出信息,对错误信息信息没有直接处理能力。然后,传递给下一个命令,作为标准的输入。

管道命令的输出说明:



【指令1】正确输出,作为【指令2】的输入,然后【指令2】的输出作为【指令3】的输入 ,【指令3】输出就会直接显示在屏幕上面了。

通过管道之后【指令1】和【指令2】的正确输出不在屏幕上显示。

【提醒注意】:

1. 管道命令只处理前一个命令正确输出,不处理错误输出。

2. 管道命令右边命令,必须能够接收标准输入流命令才行。

----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:操作系统学习,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了