Linux系统编程中的“管道”

190 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20 天,点击查看活动详情

一、进程间通信的常用方式,特征

  • 管道:简单

  • 信号:开销小

  • mmap映射:非血缘关系进程间

  • socket(本地套接字):稳定

二、管道

  • 实现原理: 内核借助环形队列机制,使用内核缓冲区实现。

  • 特质:

    1. 伪文件

    2. 管道中的数据只能一次读取。

    3. 数据在管道中,只能单向流动。

  • 局限性:

    1. 自己写,不能自己读。

    2. 数据不可以反复读。

    3. 半双工通信。

    4. 血缘关系进程间可用。

管道原理图 管道原理图.png

三、pipe函数 创建,并打开管道。

int pipe(int fd[2]);

  • 参数:

    fd[0]: 读端。

    fd[1]: 写端。

  • 返回值:

    成功: 0

    失败: -1 errno

四、管道的读写行为

  • 读管道:
  1. 管道有数据,read返回实际读到的字节数。

  2. 管道无数据:

    1)无写端,read返回0 (类似读到文件尾)

    2)有写端,read阻塞等待。

  • 写管道:
  1. 无读端, 异常终止。 (SIGPIPE导致的)

  2. 有读端:

    1) 管道已满, 阻塞等待

    2) 管道未满, 返回写出的字节个数。

管道通信 管道通信.png

示例程序

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

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

int main(int argc, char *argv[])
{
    int ret;
    int fd[2];
    pid_t pid;

    char *str = "hello pipe\n";
    char buf[1024];

    ret = pipe(fd);
    if (ret == -1)
        sys_err("pipe error");

    pid = fork();
    if (pid > 0) {
        close(fd[0]);       // 关闭读段
        sleep(3);
        //write(fd[1], str, strlen(str));
        close(fd[1]);
    } else if (pid == 0) {
        close(fd[1]);       // 子进程关闭写段
        ret = read(fd[0], buf, sizeof(buf));
        printf("child read ret = %d\n", ret);
        write(STDOUT_FILENO, buf, ret);

        close(fd[0]);
    }

	return 0;
}

五、管道分类

  • pipe管道: 用于有血缘关系的进程间通信。 ps aux | grep ls | wc -l

    父子进程间通信:

    兄弟进程间通信:

  • fifo管道:可以用于无血缘关系的进程间通信。

    命名管道: mkfifo

    无血缘关系进程间通信:

    1. 读端,open fifo O_RDONLY

    2. 写端,open fifo O_WRONLY