Linux进程间通信之管道(pipe)

管道是UNIX系统IPC的最古老的形式,并且所有UNIX系统都提供此种通信机制。但是管道存在如下特点:

  1. 管道是半双工的。
  2. 管道只能用在具有公共祖先的进程之间。
管道的创建
管道是通过调用pipe函数创建的:
#include <unistd.h>

int pipe(int filedes[2]);
参数filedes[2]是两个文件描述符,filedes[0]为读打开,filedes[1]为写打开。filedes[1]的输出是filedes[0]的输入。

管道的实现机制
管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。
这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。
一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。
当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。
当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

从原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。
最开始的时候,上面的两个箭头都连接在同一个进程Process 1上(连接在Process 1上的两个箭头)。当fork复制进程的时候,会将这两个连接也复制到新的进程(Process 2)。
随后,每个进程关闭自己不需要的一个连接 (两个黑色的箭头被关闭; Process 1关闭从PIPE来的输入连接,Process 2关闭输出到PIPE的连接).
这样,剩下的红色连接就构成了如上图的PIPE。

管道的读写
  •  对于写管道:
  写入管道的数据按到达次序排列。如果管道满,则对管道的写被阻塞,直到管道的数据被读操作读取。
        对于写操作,如果一次write调用写的数据量小于管道容量,则写必须一次完成,
        即如果管道所剩余的容量不够,write被阻塞直到管道的剩余容量可以一次写完为止。如果write调用写的数据量大于管道容量,则写操作分多次完成。
        如果用fcntl设置管道写端口为非阻塞方式,则管道满不会阻塞写,而只是对写返回0。
  • 对于读管道:
  读操作按数据到达的顺序读取数据。已经被读取的数据在管道内不再存在,这意味着数据在管道中不能重复利用。
        如果管道为空,且管道的写端口是打开状态,则读操作被阻塞直到有数据写入为止。
        一次read调用,如果管道中的数据量不够read指定的数量,则按实际的数量读取,并对read返回实际数量值。
        如果读端口使用fcntl设置了非阻塞方式,则当管道为空时,read调用返回0。
  • 对于管道的关闭:
  如果管道的读端口关闭,那么在该管道上的发出写操作调用的进程将接收到一个SIGPIPE信号。
        关闭写端口是给读端口一个文件结束符的唯一方法。对于写端口关闭后,在该管道上的read调用将返回0。

管道应用实例

  • 实例一:用于shell
            管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入。
            比如,当在某个shell程序键入who│wc -l后,相应shell程序将创建who以及wc两个进程和这两个进程间的管道。
  • 实例二:用于具有亲缘关系的进程间通信
             下面的程序创建了一个从父进程到子进程的管道。并且父进程经由管道向子进程传送数据。
  1 #include <stdlib.h>
  2 #include <stdio.h>
  3 #include <unistd.h>
  4 #include <signal.h>
  5 #include <string.h>
  6 
  7 #define MAXLEN  128
  8 
  9 int main()
 10 {
 11     int ret = 0;
 12     int filedes[2] = {-1, -1};
 13     char buf[MAXLEN];
 14 
 15     ret = pipe(filedes);
 16     if (ret == -1) {
 17         perror("create pipe error");
 18         return -1;
 19     }
 20 
 21     ret = fork();
 22     if (ret < 0) {  // fork error
 23         perror("fork error");
 24         return -1;
 25     } else if (ret > 0) {   // parent process
 26         close(filedes[0]);
 27 
 28         write(filedes[1], "hello world\n", 12);
 29         
 30     } else {    // child process
 31         close(filedes[1]);
 32 
 33         ret = read(filedes[0], buf, MAXLEN);
 34         if (ret < 0) {
 35             perror("read pipe error");
 36             return -1;
 37         }
 38         write(STDOUT_FILENO, buf, ret);
 39     }
 40 
 41     return 0;
 42 }
程序执行结果如下:
[shenlx@CentOS pipe]$ ./a.out 
hello world
管道的局限性
管道的主要局限性正体现在它的特点上:
  • 只支持单向数据流;
  • 只能用于具有亲缘关系的进程之间;
  • 没有名字;
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值