使用 Linux 命名管道 (FIFO) 实现无血缘关系进程间通信
本文介绍 Linux 命名管道 (FIFO) 的概念、创建方法及在无血缘关系进程间通信中的应用。通过命令行和 C 语言示例展示了 mkfifo 函数及读写操作,涵盖非阻塞打开、select/poll 监控等高级特性,并总结了注意事项与实际应用场景。

本文介绍 Linux 命名管道 (FIFO) 的概念、创建方法及在无血缘关系进程间通信中的应用。通过命令行和 C 语言示例展示了 mkfifo 函数及读写操作,涵盖非阻塞打开、select/poll 监控等高级特性,并总结了注意事项与实际应用场景。

在 Linux 系统中,进程间通信 (IPC) 是编程中常见的需求。传统上,有血缘关系的进程 (如父子进程) 可以通过管道进行通信,但无血缘关系的进程则需要其他 IPC 机制。本文将介绍如何使用 Linux 命名管道 (FIFO) 实现无血缘关系进程间的通信,展示其原理、实现方法和实际应用场景。
命名管道 (FIFO, First In First Out) 是一种特殊的文件类型,它允许不相关的进程之间进行通信。与匿名管道不同,命名管道不要求通信的进程之间有血缘关系,因此可以用于任意两个进程之间的通信。
命名管道的主要特点包括:
可以使用 mkfifo 命令在命令行中创建命名管道:
mkfifo my_pipe
这将在当前目录下创建一个名为 my_pipe 的命名管道文件。
在 C 语言中,可以使用 mkfifo 函数来创建命名管道:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
const char *fifo_path = "/tmp/my_fifo";
// 创建命名管道
if (mkfifo(fifo_path, 0666) == -1) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
printf("Named pipe created: %s\n", fifo_path);
return 0;
}
下面是一个写进程的示例代码,它向命名管道写入数据:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main() {
const char *fifo_path = "/tmp/my_fifo";
const char *message = "Hello from writer process!";
// 以只写方式打开命名管道
int fd = open(fifo_path, O_WRONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 向命名管道写入数据
write(fd, message, strlen(message));
printf("Message sent: %s\n", message);
// 关闭命名管道
close(fd);
return 0;
}
下面是一个读进程的示例代码,它从命名管道读取数据:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define BUFFER_SIZE 256
int main() {
const char *fifo_path = "/tmp/my_fifo";
char buffer[BUFFER_SIZE];
// 以只读方式打开命名管道
int fd = open(fifo_path, O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 从命名管道读取数据
int bytes_read = read(fd, buffer, BUFFER_SIZE - 1);
if (bytes_read == -1) {
perror("read");
exit(EXIT_FAILURE);
}
buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾
printf("Message received: %s\n", buffer);
// 关闭命名管道
close(fd);
return 0;
}
要运行这个示例,首先需要创建命名管道,然后分别编译和运行写进程和读进程。需要注意的是,读进程应该先于写进程启动,因为读进程在打开命名管道时会阻塞,直到有另一个进程以写方式打开同一个命名管道。
# 创建命名管道
mkfifo /tmp/my_fifo
# 编写进程
gcc writer.c -o writer
./writer
# 读进程
gcc reader.c -o reader
./reader
默认情况下,以只读或只写方式打开命名管道会导致进程阻塞,直到有另一个进程以相反的方式打开同一个命名管道。为了避免阻塞,可以在打开命名管道时指定 O_NONBLOCK 标志:
// 非阻塞方式打开命名管道
int fd = open(fifo_path, O_RDONLY | O_NONBLOCK);
可以使用 select 或 poll 函数来监控命名管道的可读性或可写性,从而避免阻塞:
#include <sys/select.h>
#include <poll.h>
// 使用 select 监控命名管道
fd_set read_fds;
struct timeval timeout;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
timeout.tv_sec = 5; // 5 秒超时
timeout.tv_usec = 0;
int ret = select(fd + 1, &read_fds, NULL, NULL, &timeout);
if (ret > 0) {
if (FD_ISSET(fd, &read_fds)) {
// 命名管道可读
// 执行读取操作
}
}
// 使用 poll 监控命名管道
struct pollfd fds;
fds.fd = fd;
fds.events = POLLIN; // 监控可读事件
int ret = poll(&fds, 1, 5000); // 5 秒超时
if (ret > 0) {
if (fds.revents & POLLIN) {
// 命名管道可读
// 执行读取操作
}
}
命名管道在实际开发中有多种应用场景,例如:
本文介绍了 Linux 命名管道 (FIFO) 的基本概念、创建方法以及如何使用它来实现无血缘关系进程间的通信。通过示例代码,展示了如何创建命名管道、打开和关闭命名管道,以及如何使用命名管道进行进程间通信。此外,还介绍了命名管道的高级特性和注意事项。
命名管道是一种简单而有效的进程间通信机制,特别适用于不相关的进程之间的通信。通过合理使用命名管道,可以实现进程间的数据交换和同步,提高程序的灵活性和可扩展性。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online