舟山市文章资讯

Linux进程间通信之命名管道(FIFO):跨进程通信的实用方案

2026-03-22 10:09:01 浏览次数:2
详细信息
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文件 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); }

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

相关推荐