欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

进程间通信IPC——管道(匿名管道)

程序员文章站 2024-03-01 13:13:22
...

1、概念

管道是个伪文件,本质上是内核使用环形队列机制,借助内核缓冲区实现。

2、特性

1)特征

  • 管道本质为内核缓冲区,用户态来看是一个伪文件
  • 由于是伪文件,所以对于管道有两个文件描述符印用,一个表示读端,一个表示写端
  • 管道是基于字节流实现通信的
  • 管道依赖于文件系统,其生命周期随进程
  • 管道本身自带同步互斥
  • fork()进程的时候,子进程会继承父进程的管道与其描述符

2)局限性

  • 管道采用半双工通信方式,数据只能在单方向上流动
  • 管道中数据不可反复读取。一旦读走,管道中不再存在
  • 管道必须作用于有血缘关系的进程之间

注意:
由于管道是借助内核缓冲区(4k)实现。所以,向管道中写入数据的大小要控制不能超过 pipe_buf[ ] (4096个字节)
查看内核缓冲区大小通过 ulimit -a 命令查看
进程间通信IPC——管道(匿名管道)

3、接口

UNIX中创建匿名管道的函数如下:

#include <unistd.h>

int pipe(int filedes[2]);

返回:
    成功: 0
    失败:-1

参数:
	filedes[2]:文件描述符数组

通过调用pipe函数,管道会产生两个文件描述符:

  • fds[0] 读取数据
  • fds[1] 写入数据

从文件描述符角度看管道:
进程间通信IPC——管道(匿名管道)

4、管道读写规则

1)读管道

  • 管道中有数据,read返回实际读到的字节数。
  • 管道中没有数据:
    (1)管道写端被全部关闭,read读完管道中剩余数据后会返回0 (好像读到文件结尾)
    (2)写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)

2)写管道

  • 读端全部被关闭,写端执行写时进程会异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)
  • 管道读端没有全部关闭:
    (1)管道已满,write阻塞
    (2)管道未满,write将数据写入,并返回实际写入的字节数

5、示例

示例一:从键盘读取数据,写⼊入管道,读取管道,写到屏幕

/*************************************************************************
    > File Name: pipe.c
    > Describ: 从键盘读取数据,写入管道,读取管道,写到屏幕
    > Author: hang
    > Mail: aaa@qq.com 
    > Created Time: Wed 26 Feb 2018 11:03:30 PM CST
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(void)
{
    int fds[2];
    char buf[100];
    int len;

    if(pipe(fds) == -1)
    {
        perror("make pipe");
        exit(1);
    }

    //read from stdin
    while(fgets(buf, 100, stdin)){
        len = strlen(buf);
        //写入管道
        if(write(fds[1], buf, len) != len){
            perror("write to pipe");
            break;
        }
        memset(buf, 0x00, sizeof(buf));

        //读取管道
        if((len=read(fds[0], buf, 100)) == -1){
            perror("read from pipe");
            break;
        }

        //write to stdout
        if(write(1, buf, len) != len){
            perror("write to stdout");
            break;
        }
    }

    return 0;
}

程序运行结果如下:
进程间通信IPC——管道(匿名管道)
示例二:使用管道实现父子进程间通信,完成:ls | wc –l。假定父进程实现ls,子进程实现wc。

/*************************************************************************
    > File Name: pipe_ls_wc.c
    > Describ: 使用管道实现父子进程间通信,完成:ls | wc –l
    > Author: hang
    > Mail: aaa@qq.com 
    > Created Time: Wed 26 Feb 2018 11:12:30 PM CST
 ************************************************************************/

// 利用管道进行通信
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(void)
{
    int fds[2];
    if(pipe(fds) < 0) // 创建管道
    {   
        perror("pipe error");
        exit(1);
    }   
            
    pid_t pid = fork(); // 创建子进程
    if(pid<0)
    {   
        perror("fork error");
        exit(1);
    }else if(pid==0) // 子进程
    {             
        close(fds[0]);   // 关闭读
        dup2(fds[1], STDOUT_FILENO);// 输出定位到管道的写端
        close(fds[1]);  // 关闭写端
        execlp("ls", "ls", NULL); // 用ls程序替换当前进程
        perror("execlp error");
        exit(1);
    }else
    {
        close(fds[1]);  // 关闭管道的写端
        dup2(fds[0], STDIN_FILENO);// 将父进程的输入重定向到管道的读端
        close(fds[0]);  // 关闭管道的读端
        execlp("wc", "wc", "-l", NULL); // wc替换
        perror("execlp error");
        exit(1);
    }
}

程序运行结果如下:
进程间通信IPC——管道(匿名管道)
分析:
进程间通信IPC——管道(匿名管道)
进程间通信IPC——管道(匿名管道)