进程通信之管道

文章目录

管道

  • 用于进程间通信(IPC)的单向数据通道
  • 数据在内核缓冲区中传递,不写入文件系统
  • 遵循先进先出(FIFO)原则
  • 命令行中的管道
    • | 符号创建管道,将 ps 命令的输出作为 grep 命令的输入
bash 复制代码
ps -elf | grep ./test

无名管道(匿名管道)

创建无名管道

  • 写入管道写入端的数据由内核缓冲,直到从管道的读取端读取
c 复制代码
#include <unistd.h>
int pipe(int pipefd[2]);
  • 数组 pipefd 用于返回两个引用管道末端的文件描述符
    • pipefd[0]:读取端文件描述符
    • pipefd[1]:写入端文件描述符
  • 返回 0 成功,-1 失败

无名管道使用模式

  • 单进程管道(无意义)
  • 父子进程管道(常用)

进程间管道通信demo

c 复制代码
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    int pipefd[2];
    pid_t pid;
    char buf[128];
    
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    
    pid = fork();
    if (pid == 0) {
        // 子进程:读取数据
        close(pipefd[1]);  // 关闭写端
        read(pipefd[0], buf, sizeof(buf));
        printf("Child received: %s\n", buf);
        close(pipefd[0]);
    } else {
        // 父进程:写入数据
        close(pipefd[0]);  // 关闭读端
        write(pipefd[1], "Hello from parent", 18);
        close(pipefd[1]);
        wait(NULL);
    }
    return 0;
}

管道特性

读写顺序

  • 进程执行顺序由系统调度决定,读写无严格顺序
  • 同一时刻只有一个写端和一个读端(单工通信)

读端阻塞

c 复制代码
// 问题示例
int pfd[2];
pipe(pfd);

fork();
// 子进程关闭所有管道端
close(pfd[0]);
close(pfd[1]);

// 父进程读取(会阻塞)
read(pfd[0], buf, sizeof(buf));  // 阻塞!
  • 阻塞原因:读端需要所有写端都关闭才能读到 EOF

管道破裂

  • 向没有读端的管道写入数据会导致管道破裂
  • 内核向进程发送 SIGPIPE 信号
  • 默认处理:终止进程
  • 预防:检查 write() 返回值或捕获 SIGPIPE 信号

无名管道实现命令管道(模拟 ps | grep)

思路

  • 创建无名管道
  • 创建第一个子进程,执行 ps 命令
  • 将 ps 的标准输出重定向到管道写端
  • 创建第二个子进程,执行 grep 命令
  • 将 grep 的标准输入重定向到管道读端

关键函数

c 复制代码
int dup2(int oldfd, int newfd);
  • 将 newfd 指向 oldfd 相同的文件
  • 常用于标准输入/输出重定向

实现代码

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
    int pfd[2];
    
    // 1. 创建管道
    if (pipe(pfd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    
    // 2. 创建 ps 进程
    if (fork() == 0) {
        close(pfd[0]);  // 关闭读端
        dup2(pfd[1], STDOUT_FILENO);  // 标准输出重定向到管道写端
        close(pfd[1]);
        execlp("ps", "ps", "-elf", NULL);
        perror("execlp ps");
        exit(EXIT_FAILURE);
    }
    
    // 3. 创建 grep 进程
    if (fork() == 0) {
        close(pfd[1]);  // 关闭写端
        dup2(pfd[0], STDIN_FILENO);   // 标准输入重定向到管道读端
        close(pfd[0]);
        execlp("grep", "grep", "systemd", NULL);
        perror("execlp grep");
        exit(EXIT_FAILURE);
    }
    
    // 4. 父进程关闭管道并等待子进程
    close(pfd[0]);
    close(pfd[1]);
    wait(NULL);
    wait(NULL);
    
    return 0;
}

当把stdio流重写向到管道以后,将会导致缓冲区的类型也会发生改变,由原本的行缓冲变成块缓冲

命名管道

  • 命名管道(FIFO),先进先出特殊文件
  • FIFO 特殊文件(命名管道)类似于管道,不同之处在于它是作为文件系统的一部分访问的
  • 可以由多个进程打开以进行读取或写入
  • 当进程通过 FIFO 交换数据时,内核在内部传递所有数据,而不会将其写入文件系统
  • 因此, FIFO 特殊文件在文件系统上没有内容

命名管道的创建

命令行创建

bash 复制代码
mkfifo -m 0666 myfifo
ls -l
# 输出:prw-rw-rw- 1 user user 0 ... myfifo
  • -m是自定义权限,可以看到权限位是0666(rw-rw-rw-)

mkfifo 默认不受 umask 影响

函数创建

c 复制代码
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
  • pathname: 路径
  • mode: 权限

命名管道通信

  • 发送端
c 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main() {
    int fd;
    char *fifo = "/tmp/my_fifo";
    char msg[] = "Hello from sender";
    
    // 打开管道(只写)
    fd = open(fifo, O_WRONLY);
    write(fd, msg, strlen(msg) + 1);
    close(fd);
    
    return 0;
}
  • 接收端
c 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd;
    char *fifo = "/tmp/my_fifo";
    char buffer[1024];
    
    // 打开管道(只读)
    fd = open(fifo, O_RDONLY);
    read(fd, buffer, sizeof(buffer));
    printf("Received: %s\n", buffer);
    close(fd);
    
    return 0;
}

无名管道VS匿名管道

特性 无名管道 命名管道
可见性 仅相关进程 所有进程
创建方式 pipe() mkfifo()
持久性 进程结束消失 文件系统存在
使用场景 父子/兄弟进程 任意进程间
  • 读阻塞:确保写端正确关闭
  • 管道破裂:检查读端是否存在
  • 数据完整性:小数据可能合并,大数据分块传输
  • 缓冲区大小:Linux 默认 64KB,可通过 fcntl() 查询
相关推荐
Trouvaille ~6 天前
【Linux】进程间通信(二):命名管道与进程池架构实战
linux·c++·chrome·架构·进程间通信·命名管道·进程池
Trouvaille ~7 天前
【Linux】进程间通信(三):共享内存深度剖析与System V IPC机制
linux·c++·操作系统·管道·进程间通信·信号量·system v
予枫的编程笔记8 天前
【基础概念】管道、消息队列、共享内存、信号
消息队列·共享内存·信号·管道
Trouvaille ~9 天前
【Linux】进程间通信(一):IPC基础与管道机制深度剖析
linux·运维·c++·管道·进程间通信·匿名管道·半双工
定偶11 天前
Linux进程管理和进程间通信机制
c语言·进程·共享内存·管道·信号量·消息列队
我在人间贩卖青春18 天前
进程通信之信号量
进程通信·信号量
HalvmånEver22 天前
Linux:命名管道(进程间通信六)
linux·运维·服务器·c++·命名管道·管道pipe
HalvmånEver22 天前
Linux:命名管道实现IPC(进程间通信七)
linux·运维·服务器·ipc·命名管道·管道pipe
努力的小帅1 个月前
Linux_进程间通信(Linux入门到精通)
linux·c++·centos·共享内存·进程通信·命名管道·管道的学习