当前位置:首页>Linux>在Linux进程/线程中,如何使用Pipe(匿名管道)和FIFO(命名管道)?

在Linux进程/线程中,如何使用Pipe(匿名管道)和FIFO(命名管道)?

  • 2026-10-08 22:40:38
在Linux进程/线程中,如何使用Pipe(匿名管道)和FIFO(命名管道)?

说到匿名管道(pipe)和命名管道(fifo),更偏进于进程间通信,不是进程内线程通信的主力。

但在实际项目中也非常常见。

实际项目高频场景,如下方描述

场景A:守护进程+子进程工具

  • 比如主程序启动一个外部工具进程,用它去做某些事情:

  • 主进程发命令给子进程

  • 子进程把执行结果返回给主进程

  • 这时通常用pipe.

场景B:后台服务+调试命令工具

  • 比如后台守护进程一直运行,工程师有时需要通过一个命令行小工具给它发命令:

  • reload、dump_status、enable_debug

  • 两个进程互不相关,这时常用FIFO.

核心概念

Pipe(匿名管道)

  • 只能用于有亲缘关系的进程,如:父子进程

  • pipe(fd)创建后,配合(fork)使用

  • 半双工,一端读一端写

FIFO(命名管道)

  • 本质也是管道

  • 但它在文件系统里有名字

  • 可以给无亲缘关系的进程使用

常用API

头文件:

#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>

Pipe

int pipe(int fd[2]);
  • fd[0]:    读端

  • fd[1]:    写端

FIFO

int mkfifo(const char *pathname,mode_t mode);

配合使用:

open()、read()、write()、close()

通俗理解:

你可以把pipe/FIFO看成一个”内核帮你保管的传送带“:

  • 写端往传送带放数据

  • 读端往传送带拿数据

特点是:

  • 按顺序传输

  • 是字节流

  • 不关心你写的是”消息1“还是”消息2“

  • 更像文件读写

代码演示1:

Pipe父子进程传命令结果

场景:父进程向子进程发送一个"采集状态查询命令", 子进程返回结果。

vim 02_pipe_fifo.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/wait.h>/* * 这里做双向通信: * parent -> child : 发送命令 * child  -> parent: 返回结果 * * 所以用了两个pipe */int main(void){    int pipe_cmd[2];   // 父发命令给子    int pipe_resp[2];  // 子返回结果给父if (pipe(pipe_cmd) == -1 || pipe(pipe_resp) == -1) {        perror("pipe");return 1;    }    pid_t pid = fork();if (pid < 0) {        perror("fork");return 1;    }if (pid == 0) {        /* 子进程 */        close(pipe_cmd[1]);   // 子进程不用命令写端        close(pipe_resp[0]);  // 子进程不用响应读端        char cmd[128] = {0};read(pipe_cmd[0], cmd, sizeof(cmd));printf("[child] received cmd: %s\n", cmd);        /*         * 模拟执行命令         * 实际项目中可能是查询设备状态、读取传感器值等         */        char resp[128] = {0};if (strcmp(cmd, "GET_STATUS") == 0) {            snprintf(resp, sizeof(resp), "status=OK,temp=42");        } else {            snprintf(resp, sizeof(resp), "unknown command");        }        write(pipe_resp[1], resp, strlen(resp) + 1);        close(pipe_cmd[0]);        close(pipe_resp[1]);exit(0);    } else {        /* 父进程 */        close(pipe_cmd[0]);   // 父进程不用命令读端        close(pipe_resp[1]);  // 父进程不用响应写端        const char *cmd = "GET_STATUS";        write(pipe_cmd[1], cmd, strlen(cmd) + 1);        char resp[128] = {0};read(pipe_resp[0], resp, sizeof(resp));printf("[parent] child response: %s\n", resp);        close(pipe_cmd[1]);        close(pipe_resp[0]);wait(NULL);    }return 0;}

编译运行:

tammy@ubuntu:~/cprogramer/thread_pro$ gcc 02_pipe_fifo.c -o 02_pipe_fifo -lpthreadtammy@ubuntu:~/cprogramer/thread_pro$ ./02_pipe_fifo[child] received cmd: GET_STATUS[parent] child response: status=OK,temp=42

代码演示2:FIFO 后台服务接收调试命令

场景:后台服务通过/tmp/device_cmd_fifo接收外部调试命令。

服务端:server_fifo.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#define FIFO_PATH "/tmp/device_cmd_fifo"int main(void){    /* 创建FIFO,如果已经存在也没关系 */if (mkfifo(FIFO_PATH, 0666) == -1) {        perror("mkfifo maybe already exists");    }printf("[server] waiting command on %s ...\n", FIFO_PATH);while (1) {        /*         * O_RDONLY 打开会阻塞,直到有写端连接         * 适合服务端一直等待客户端命令         */        int fd = open(FIFO_PATH, O_RDONLY);if (fd < 0) {            perror("open");continue;        }        char buf[128] = {0};        int n = read(fd, buf, sizeof(buf) - 1);if (n > 0) {printf("[server] received cmd: %s\n", buf);if (strcmp(buf, "reload") == 0) {printf("[server] do reload config ...\n");            } elseif (strcmp(buf, "dump_status") == 0) {printf("[server] dump current status ...\n");            } else {printf("[server] unknown cmd\n");            }        }        close(fd);    }return 0;}

客户端:client_fifo.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#define FIFO_PATH "/tmp/device_cmd_fifo"int main(int argc, char *argv[]){if (argc != 2) {printf("usage: %s <cmd>\n", argv[0]);printf("example: %s reload\n", argv[0]);return 1;    }    int fd = open(FIFO_PATH, O_WRONLY);if (fd < 0) {        perror("open");return 1;    }    write(fd, argv[1], strlen(argv[1]) + 1);    close(fd);return 0;}

编译:

tammy@ubuntu:~/cprogramer/thread_pro$ gcc 03_server_fifo.c -o 03_server_fifotammy@ubuntu:~/cprogramer/thread_pro$ gcc 03_client_fifo.c -o 03_client_fifotammy@ubuntu:~/cprogramer/thread_pro$ ls01_mutex_cond.c  03_client_fifo    03_server_fifo02_pipe_fifo.c   03_client_fifo.c  03_server_fifo.c

运行终端1(server程序):

tammy@ubuntu:~/cprogramer/thread_pro$ ./03_server_fifomkfifo maybe already exists: File exists[server] waiting command on /tmp/device_cmd_fifo ...[server] received cmd: reload[server] do reload config ...[server] received cmd: dump_status[server] dump current status ...

运行 终端2(client程序):

tammy@ubuntu:~/cprogramer/thread_pro$ ./03_client_fifo reloadtammy@ubuntu:~/cprogramer/thread_pro$ ./03_client_fifo dump_status

项目里怎么用?

Pipe 适合

  • 父子进程

  • 主程序拉起工具进程

  • shell 风格串联处理

FIFO 适合

  • 后台守护进程接收简单命令

  • 测试工具向服务发送指令

  • 无关进程间的轻量文本通信

注意

  • 它是字节流

  • 不适合复杂结构化协议

  • 如果消息复杂,通常会自己设计协议头,或者直接用消息队列/socket

最新文章

随机文章