欢迎光临中国护送网
详情描述

Linux进程间通信:命名管道(FIFO)详解

一、什么是命名管道(FIFO)

命名管道是一种特殊的文件类型,允许无亲缘关系的进程之间进行通信。与匿名管道(pipe)不同,FIFO在文件系统中有一个真实的文件名,任何知道该文件名的进程都可以访问它。

二、FIFO的核心特性

持久性:在文件系统中可见,直到被显式删除 半双工:数据单向流动(需要两个FIFO实现双向通信) 阻塞I/O:默认读写操作会阻塞,直到另一端有操作 字节流:数据以字节流形式传输,无消息边界

三、创建与使用FIFO

创建FIFO的两种方式

1. 命令行创建

# 创建命名管道
mkfifo /tmp/myfifo

# 查看文件类型
ls -l /tmp/myfifo
# 输出:prw-r--r-- 1 user user 0 Jan 1 12:00 /tmp/myfifo
# 'p' 表示管道文件

2. 程序内创建

#include <sys/types.h>
#include <sys/stat.h>

// 创建FIFO,mode为权限(如0666)
int mkfifo(const char *pathname, mode_t mode);

四、完整示例:生产者-消费者模型

生产者程序(writer.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define FIFO_FILE "/tmp/myfifo"
#define BUFFER_SIZE 1024

int main() {
    int fd;
    char buff[BUFFER_SIZE];

    // 创建FIFO(如果不存在)
    if (mkfifo(FIFO_FILE, 0666) == -1) {
        // 可能已存在,继续尝试打开
    }

    printf("生产者启动,等待消费者连接...\n");

    // 打开FIFO进行写入(会阻塞直到消费者打开读取端)
    fd = open(FIFO_FILE, O_WRONLY);

    while (1) {
        printf("输入消息 (输入'quit'退出): ");
        fgets(buff, BUFFER_SIZE, stdin);

        // 写入FIFO
        write(fd, buff, strlen(buff)+1);

        if (strncmp(buff, "quit", 4) == 0) {
            break;
        }
    }

    close(fd);
    // unlink(FIFO_FILE); // 可选:删除FIFO文件
    return 0;
}
消费者程序(reader.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define FIFO_FILE "/tmp/myfifo"
#define BUFFER_SIZE 1024

int main() {
    int fd;
    char buff[BUFFER_SIZE];

    printf("消费者启动,等待数据...\n");

    // 打开FIFO进行读取(会阻塞直到生产者打开写入端)
    fd = open(FIFO_FILE, O_RDONLY);

    while (1) {
        // 从FIFO读取数据
        int bytes_read = read(fd, buff, BUFFER_SIZE);

        if (bytes_read > 0) {
            printf("收到消息: %s", buff);

            if (strncmp(buff, "quit", 4) == 0) {
                break;
            }
        }
    }

    close(fd);
    return 0;
}

五、编译与测试

# 编译
gcc writer.c -o writer
gcc reader.c -o reader

# 终端1:运行消费者
./reader

# 终端2:运行生产者
./writer

六、高级用法与技巧

1. 非阻塞模式
// 非阻塞方式打开FIFO
int fd = open(FIFO_FILE, O_RDONLY | O_NONBLOCK);

// 或通过fcntl设置
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
2. 多客户端服务器模型
// 服务器处理多个客户端
#include <errno.h>

void server_handle_multiple_clients() {
    int fd, bytes;
    char buffer[256];

    mkfifo("/tmp/server_fifo", 0666);

    while (1) {
        // 打开服务器FIFO(阻塞等待客户端)
        fd = open("/tmp/server_fifo", O_RDONLY);

        // 读取客户端请求
        while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) {
            printf("收到请求: %s\n", buffer);

            // 创建唯一的响应FIFO名
            char response_fifo[50];
            sprintf(response_fifo, "/tmp/client_%s_fifo", buffer);

            // 打开客户端FIFO并发送响应
            int resp_fd = open(response_fifo, O_WRONLY);
            write(resp_fd, "响应数据", 10);
            close(resp_fd);
        }

        close(fd);
    }
}
3. 双向通信实现
// 进程A创建两个FIFO
mkfifo("/tmp/AtoB", 0666);
mkfifo("/tmp/BtoA", 0666);

// 进程A:向AtoB写,从BtoA读
// 进程B:向BtoA写,从AtoB读

七、FIFO的限制与注意事项

容量限制:Linux默认64KB(可配置)

原子性:写入数据量≤PIPE_BUF(通常4096字节)时保证原子性

阻塞问题

  • 读空FIFO会阻塞,直到有数据写入
  • 写满FIFO会阻塞,直到有数据被读取
  • 所有写入端关闭后,读取返回0
  • 所有读取端关闭后,写入会触发SIGPIPE信号

清理问题:FIFO文件需要手动删除

// 删除FIFO文件
unlink("/tmp/myfifo");

八、与其他IPC方式的对比

特性 命名管道 匿名管道 消息队列 共享内存
无亲缘关系
文件系统可见
通信方向 半双工 半双工 全双工 全双工
性能
数据格式 字节流 字节流 消息 字节流

九、实际应用场景

Shell命令协同

# 终端1:创建并写入FIFO
mkfifo /tmp/myfifo
echo "Hello World" > /tmp/myfifo
终端2:从FIFO读取

cat < /tmp/myfifo


2. **日志收集系统**
```c
// 多个进程向同一个FIFO写入日志
// 日志收集进程从FIFO读取并统一处理
进程池任务分发
// 主进程向FIFO写入任务
// 工作进程从FIFO读取并执行

十、最佳实践

错误处理:始终检查系统调用返回值 信号处理:处理SIGPIPE等可能信号 资源清理:程序退出前删除FIFO文件 权限控制:设置适当的文件权限(如0600) 超时机制:使用select/poll防止永久阻塞
// 使用poll实现超时读取
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;

int ret = poll(fds, 1, 3000); // 3秒超时
if (ret > 0 && (fds[0].revents & POLLIN)) {
    read(fd, buffer, size);
}

命名管道作为一种简单有效的进程间通信机制,特别适用于单向数据流无亲缘关系进程的通信场景。虽然功能相对简单,但在许多系统编程场景中仍然非常实用。

相关帖子
小区公共充电桩的收费标准、支付方式以及日常维护责任是如何规定的?
小区公共充电桩的收费标准、支付方式以及日常维护责任是如何规定的?
海外留学归国人员在国内哪些城市可以享受到专门的人才引进落户补贴?
海外留学归国人员在国内哪些城市可以享受到专门的人才引进落户补贴?
舟山市精准获客软件@品牌网站建设设计,定制开发
舟山市精准获客软件@品牌网站建设设计,定制开发
舟山市病人跨省市转运120救护车-专业接送病人服务车
舟山市病人跨省市转运120救护车-专业接送病人服务车
舟山市网站建设推广-网站改版,服务可靠
舟山市网站建设推广-网站改版,服务可靠
株洲市安卓app开发@网站建设服务公司,网站制作
株洲市安卓app开发@网站建设服务公司,网站制作
2026年,哪些个人收入变化会直接触发公积金缴费基数的重新核定?
2026年,哪些个人收入变化会直接触发公积金缴费基数的重新核定?
晋中市病人转运服务电话-重症病人转院租救护车,车内设备齐全
晋中市病人转运服务电话-重症病人转院租救护车,车内设备齐全
新乡市救护车跨省护送病人回家-救护车转院病人返乡,收费合理
新乡市救护车跨省护送病人回家-救护车转院病人返乡,收费合理
2026年慢性病待遇资格认定的审核标准与所需材料是否有新的变化?
2026年慢性病待遇资格认定的审核标准与所需材料是否有新的变化?
威海市android软件开发#网站开发制作,服务可靠
威海市android软件开发#网站开发制作,服务可靠
龙岩市手机网站建设@网站定制服务,模板建站
龙岩市手机网站建设@网站定制服务,模板建站
运营商在2026年如何平衡网络建设投入与电话卡资费下调之间的关系?
运营商在2026年如何平衡网络建设投入与电话卡资费下调之间的关系?
从环保角度看,阶梯电价水价气价政策是如何引导公众节约资源的?
从环保角度看,阶梯电价水价气价政策是如何引导公众节约资源的?
延安市企业数字化@品牌网站建设开发,专业建站
延安市企业数字化@品牌网站建设开发,专业建站
洛阳市跨省120救护车出租-重症监护救护车出租
洛阳市跨省120救护车出租-重症监护救护车出租
2026年常见的工会福利类型有哪些,它们如何惠及普通员工?
2026年常见的工会福利类型有哪些,它们如何惠及普通员工?
“全抛世代”对传统家庭观念与亲情纽带构成了怎样的挑战与重新定义?
“全抛世代”对传统家庭观念与亲情纽带构成了怎样的挑战与重新定义?
九江市智能响应式网站建设#品牌网站制作设计,提供一站式建站服务
九江市智能响应式网站建设#品牌网站制作设计,提供一站式建站服务
保留哪些关键证据,能最大程度帮助你在外卖索赔纠纷中占据优势?
保留哪些关键证据,能最大程度帮助你在外卖索赔纠纷中占据优势?