
在 Linux 进程间通信的学习中,匿名管道作为入门级的 IPC 方式,让我们理解了 “内核缓冲区 + 一切皆文件” 的设计思想,但它有一个致命的限制 ——只能用于具有亲缘关系的进程间通信。而命名管道(FIFO)作为匿名管道的 “升级版”,完美突破了这一限制,让任意无亲缘关系的进程也能实现高效的管道通信。 命名管道也叫 FIFO(First In First Out),是一种特殊的管道文件,它拥有磁盘级的文件名,通过文件系统实现进程间的关联,操作接口与匿名管道完全兼容,却能覆盖更广泛的通信场景。本文将从命名管道的核心概念出发,一步步拆解其原理、创建方式、打开规则、实战应用,结合硬核代码和通俗讲解,让你彻底吃透命名管道,掌握跨进程通信的关键技能。下面就让我们正式开始吧!

在学习命名管道之前,我们先回顾一下匿名管道的核心局限性,这也是命名管道诞生的根本原因。
匿名管道基于文件描述符继承实现通信,只有通过fork()创建的子进程(或兄弟进程)才能继承父进程的管道描述符,因此只能用于有共同祖先的亲缘进程(父 / 子、兄 / 弟)。
但在实际开发中,我们更多的需求是让完全独立的无亲缘进程通信:比如一个服务进程和一个客户端进程、两个独立启动的应用程序、不同用户的进程之间,此时匿名管道就完全无法满足需求。
为了解决匿名管道的亲缘限制问题,Linux 设计者在匿名管道的基础上,增加了文件系统的标识,也就是命名管道文件。
命名管道的核心设计思路可以总结为:给内核中的管道缓冲区,在文件系统中分配一个唯一的文件名,任何进程只要能访问这个文件,就能通过它连接到内核中的管道缓冲区,实现进程间通信。
简单来说,匿名管道是 “无名的内核缓冲区”,只有亲缘进程能找到;命名管道是 “有名的内核缓冲区”,任何进程只要知道文件名,就能找到并使用。
命名管道是匿名管道的超集,二者的内核实现和操作语义完全一致:
read()/write()/close()等标准文件操作接口;二者的唯一区别仅在于创建和打开的方式:
pipe()函数创建,创建即打开,返回两个文件描述符;mkfifo()函数创建(生成管道文件),通过open()函数打开,获取文件描述符。可以说,只要掌握了匿名管道,学习命名管道只需要重点掌握创建和打开这两个新操作,其余内容完全可以复用。
命名管道的核心是FIFO 文件,这是一种特殊的文件类型,与普通文件、目录、设备文件并列,存在于 Linux 的文件系统中,但又有其独特的属性。
FIFO 文件仅作为进程间通信的 “标识”,它本身不存储任何数据,数据依旧存储在内核的管道缓冲区中。
当进程对 FIFO 文件执行read()/write()操作时,实际上是对内核中的管道缓冲区进行操作,FIFO 文件只是一个 “入口”。进程退出后,FIFO 文件会保留在文件系统中(除非手动删除),但内核中的管道缓冲区会被释放,数据也会丢失。
在 Linux 中,通过ls -l命令查看文件时,FIFO 文件的文件类型标识为 p(pipe 的首字母),这是区分 FIFO 文件和其他文件的关键。
例如,创建一个名为mypipe的命名管道后,执行ls -l会看到如下结果:
prw-r--r-- 1 root root 0 2月 11 15:00 mypipe其中:
p:表示文件类型为命名管道(FIFO);0:因为 FIFO 文件不存储数据,仅作为标识;rw-r--r--),用于控制进程对管道的访问权限。命名管道的通信模型非常简单,核心分为三步:
mkfifo()或命令行创建一个 FIFO 文件,作为管道的标识;open()函数打开该 FIFO 文件,获取读 / 写文件描述符;整个过程中,FIFO 文件就像一个 “桥梁”,连接了两个无亲缘进程和内核中的管道缓冲区,突破了匿名管道的亲缘限制。
命名管道有两种创建方式:命令行创建和程序中通过 mkfifo () 函数创建,前者适用于测试和手动操作,后者适用于程序开发,二者的效果完全一致。
在 Linux 终端中,直接使用mkfifo命令即可创建命名管道,语法如下:
# mkfifo [选项] 管道文件名
mkfifo mypipe常用选项:
-m:指定管道文件的权限,如mkfifo -m 0644 mypipe;umask决定(通常为 0666 & ~umask)。 创建后,通过ls -l即可看到标识为p的 FIFO 文件,通过rm 管道文件名即可删除该文件。
在 C/C++ 程序中,通过mkfifo()函数创建命名管道,这是开发中的主流方式,函数原型定义在<sys/stat.h>头文件中。
#include <sys/types.h>
#include <sys/stat.h>
// 功能:创建一个命名管道(FIFO文件)
// 参数:
// pathname - 管道文件的路径+名称,如"./mypipe"
// mode - 管道文件的权限,如0644、0666,与open()函数的mode参数一致
// 返回值:成功返回0,失败返回-1,并设置errno
int mkfifo(const char *pathname, mode_t mode);mkfifo()的mode参数会与当前进程的umask进行按位与取反运算,最终的文件权限为mode & ~umask。如果需要让管道文件的权限严格等于mode,可以先通过umask(0)将掩码置 0;pathname指定的 FIFO 文件已存在,再次调用mkfifo()会失败,errno设置为EEXIST;pathname指定的目录必须存在,否则会失败,errno设置为ENOENT;mkfifo()创建的是 FIFO 文件,不能创建普通文件,也不能覆盖已存在的普通文件。#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
int main()
{
// 将umask置0,让管道权限严格为0644
umask(0);
// 创建命名管道文件./mypipe,权限0644
if (mkfifo("./mypipe", 0644) < 0)
{
perror("mkfifo error");
return -1;
}
printf("命名管道创建成功\n");
return 0;
} 编译运行该程序后,在当前目录下执行ls -l,就能看到prw-r--r--的 FIFO 文件mypipe。
命名管道的打开规则是学习的重中之重,也是与普通文件打开的最大区别,直接决定了进程的运行行为(阻塞 / 非阻塞)。
命名管道是单向通信的,必须有一个进程以读方式打开,一个进程以写方式打开,内核才会完成管道的初始化,否则打开操作会根据是否设置非阻塞标志产生不同的结果。
命名管道的打开规则围绕open()函数的flags 参数展开,核心分为读打开和写打开两种情况,每种情况又分为阻塞模式(默认)和非阻塞模式(O_NONBLOCK)。
命名管道通过标准的open()函数打开,函数原型如下:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
int open(const char *pathname, int flags, mode_t mode); 对于命名管道,核心关注flags参数,常用取值:
如果进程以纯读方式打开命名管道,且此时没有任何进程以写方式打开该管道,则open()调用会阻塞,直到有进程以写方式打开该管道,才会返回文件描述符。
简单来说:读端先打开,会阻塞等待写端打开。
如果进程以非阻塞读方式打开命名管道,无论此时是否有进程以写方式打开该管道,open()调用都会立刻返回成功,获取读端文件描述符。
如果进程以纯写方式打开命名管道,且此时没有任何进程以读方式打开该管道,则open()调用会阻塞,直到有进程以读方式打开该管道,才会返回文件描述符。
简单来说:写端先打开,会阻塞等待读端打开。
如果进程以非阻塞写方式打开命名管道,且此时没有任何进程以读方式打开该管道,则open()调用会立刻失败,返回 - 1,errno设置为ENXIO。
为了方便记忆,我们将命名管道的打开规则整理成表格,核心记住阻塞模式下,读 / 写端相互等待;非阻塞模式下,读端一定成功,写端可能失败:
打开方式 | 无对应进程打开另一端 | 有对应进程打开另一端 | |
|---|---|---|---|
O_RDONLY(阻塞读) | 阻塞等待 | 立刻成功 | |
O_RDONLY | O_NONBLOCK(非阻塞读) | 立刻成功 | 立刻成功 |
O_WRONLY(阻塞写) | 阻塞等待 | 立刻成功 | |
O_WRONLY | O_NONBLOCK(非阻塞写) | 失败(ENXIO) | 立刻成功 |
核心口诀:堵读等写,堵写等读;非堵读必成,非堵写看端。
命名管道的打开规则,本质是为了保证管道的通信双方都存在,避免出现 “一个进程向管道写入数据,但没有进程读取” 或 “一个进程从管道读取数据,但没有进程写入” 的无效操作,是内核对管道通信的基础保障。
而非阻塞模式则为程序开发提供了灵活性,让进程可以不等待另一端,直接执行后续逻辑,适用于需要轮询或处理多任务的场景。
命名管道的读写规则与匿名管道完全一致,因为二者的底层都是内核维护的字节流缓冲区,操作语义完全相同。这里我们对核心读写规则进行重点回顾,结合命名管道的场景做简单说明,让你无缝复用知识。
命名管道的读写规则同样与是否设置非阻塞模式(O_NONBLOCK)密切相关,同时涉及原子写入、SIGPIPE 信号等关键特性,是开发中必须遵守的规则,也是面试高频考点。
read()调用会阻塞,直到有进程向管道写入数据,才会被唤醒并读取数据;read()调用立刻返回 - 1,errno设置为EAGAIN(表示资源暂时不可用,可重试)。 Linux 内核中管道缓冲区的默认大小为4096 字节(1 页),可以通过ulimit -p命令查看。当管道缓冲区被写满时:
write()调用会阻塞,直到有进程从管道读取数据,释放缓冲区空间,才会被唤醒并继续写入;write()调用立刻返回 - 1,errno设置为EAGAIN。 如果所有持有管道写端文件描述符的进程都关闭了写端,此时管道中剩余的数据可以正常读取;当数据读取完毕后,再次调用read()会返回 0,与读取普通文件到末尾的行为一致,表示 “管道已无数据,且不会再有新数据写入”。
这是命名管道通信中,读端判断写端退出的核心方式,也是实现通信结束的关键。
如果所有持有管道读端文件描述符的进程都关闭了读端,此时进程向管道写端写入数据时,内核会向该进程发送SIGPIPE 信号,该信号的默认处理方式是终止进程。
这是一个非常重要的 “坑”,在命名管道开发中,如果未处理 SIGPIPE 信号,可能导致进程意外退出。因此,实际开发中需要通过signal()或sigaction()函数捕获并处理 SIGPIPE 信号(如忽略该信号)。
Linux 内核保证了命名管道写入的原子性,规则与匿名管道一致:
PIPE_BUF的取值可以通过<limits.h>头文件查看,也可以通过命令getconf PIPE_BUF获取,不同系统的取值可能不同,但至少为 512 字节。
命名管道的读写操作完全遵循文件的操作语义,但又有管道的特殊属性,核心注意点:
read()返回 0 表示写端关闭,返回 - 1 表示出错(需结合 errno 判断是阻塞还是真错误);理论学习后,最关键的是实战。本节我们通过三个经典的实战案例,从简单到复杂,一步步实现命名管道的开发,分别是:基础读写案例、文件拷贝案例、Server-Client 通信案例,覆盖命名管道的核心使用场景。
需求:创建两个独立的程序,fifo_write.c(写端)和fifo_read.c(读端),实现写端向命名管道写入字符串,读端从命名管道读取并打印,验证命名管道的基本通信功能。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
// 错误处理宏
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
// 命名管道文件名
#define FIFO_NAME "./myfifo"
int main()
{
// 1. 创建命名管道(如果已存在,会报错,这里先判断,不存在则创建)
umask(0);
if (mkfifo(FIFO_NAME, 0644) < 0)
{
// 错误码EEXIST表示文件已存在,无需处理
if (errno != EEXIST)
ERR_EXIT("mkfifo error");
}
// 2. 以阻塞写方式打开命名管道(默认O_WRONLY,会阻塞等待读端打开)
int wfd = open(FIFO_NAME, O_WRONLY);
if (wfd < 0)
ERR_EXIT("open write error");
printf("写端成功打开命名管道,文件描述符:%d\n", wfd);
// 3. 向管道写入数据
char buf[1024] = {0};
while (1)
{
// 从键盘读取输入
printf("请输入要发送的内容:");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if (s > 0)
{
buf[s-1] = 0; // 去掉换行符
// 向管道写入数据
write(wfd, buf, strlen(buf));
// 输入quit,退出程序
if (strcmp(buf, "quit") == 0)
break;
}
}
// 4. 关闭文件描述符
close(wfd);
printf("写端关闭命名管道\n");
return 0;
}#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
// 错误处理宏
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
// 命名管道文件名,与写端保持一致
#define FIFO_NAME "./myfifo"
int main()
{
// 1. 以阻塞读方式打开命名管道(默认O_RDONLY,会阻塞等待写端打开)
int rfd = open(FIFO_NAME, O_RDONLY);
if (rfd < 0)
ERR_EXIT("open read error");
printf("读端成功打开命名管道,文件描述符:%d\n", rfd);
// 2. 从管道读取数据
char buf[1024] = {0};
while (1)
{
ssize_t s = read(rfd, buf, sizeof(buf)-1);
if (s > 0)
{
buf[s] = 0;
printf("读端读取到数据:%s\n", buf);
// 读取到quit,退出程序
if (strcmp(buf, "quit") == 0)
break;
}
else if (s == 0)
{
// s==0表示所有写端关闭
printf("所有写端已关闭,读端退出\n");
break;
}
else
{
ERR_EXIT("read error");
}
}
// 3. 关闭文件描述符
close(rfd);
// 删除命名管道文件(可选,也可以手动删除)
unlink(FIFO_NAME);
printf("读端关闭并删除命名管道\n");
return 0;
}编译:在终端中分别编译两个程序:
gcc fifo_write.c -o fifo_write
gcc fifo_read.c -o fifo_read运行:打开两个终端,分别运行读端和写端(顺序任意):
./fifo_read./fifo_write测试:在写端终端输入任意字符串,读端终端会实时打印;输入quit,双方程序退出。
quit后,会向管道写入quit,读端读取到后退出,同时删除 FIFO 文件;s==0(写端关闭),并退出程序。 需求:创建两个程序,fifo_copy_send.c(发送端)和fifo_copy_recv.c(接收端),实现发送端读取本地文件,通过命名管道发送给接收端,接收端将数据写入新文件,完成文件拷贝,验证命名管道的大数据传输能力。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
#define FIFO_NAME "./copy_fifo"
#define SRC_FILE "./src.txt" // 要拷贝的源文件
int main()
{
// 1. 创建命名管道
umask(0);
if (mkfifo(FIFO_NAME, 0644) < 0)
{
if (errno != EEXIST)
ERR_EXIT("mkfifo error");
}
// 2. 打开源文件和命名管道
int src_fd = open(SRC_FILE, O_RDONLY);
if (src_fd < 0)
ERR_EXIT("open src file error");
int wfd = open(FIFO_NAME, O_WRONLY);
if (wfd < 0)
ERR_EXIT("open fifo write error");
printf("发送端准备就绪,开始拷贝文件...\n");
// 3. 读取源文件,写入命名管道
char buf[1024] = {0};
ssize_t n = 0;
while ((n = read(src_fd, buf, sizeof(buf))) > 0)
{
write(wfd, buf, n);
}
// 4. 关闭文件描述符
close(src_fd);
close(wfd);
unlink(FIFO_NAME); // 可选删除
printf("文件拷贝完成,发送端退出\n");
return 0;
}#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
#define FIFO_NAME "./copy_fifo"
#define DST_FILE "./dst.txt" // 拷贝后的目标文件
int main()
{
// 1. 打开命名管道和目标文件
int rfd = open(FIFO_NAME, O_RDONLY);
if (rfd < 0)
ERR_EXIT("open fifo read error");
// 创建目标文件,权限0644,存在则截断
int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd < 0)
ERR_EXIT("open dst file error");
printf("接收端准备就绪,开始接收数据...\n");
// 2. 从命名管道读取数据,写入目标文件
char buf[1024] = {0};
ssize_t n = 0;
while ((n = read(rfd, buf, sizeof(buf))) > 0)
{
write(dst_fd, buf, n);
}
// 3. 关闭文件描述符
close(rfd);
close(dst_fd);
printf("文件接收完成,目标文件:%s\n", DST_FILE);
return 0;
}src.txt,写入任意内容:echo "hello world, this is fifo copy test" > src.txt;gcc fifo_copy_send.c -o send && gcc fifo_copy_recv.c -o recv;./recv和./send;dst.txt:cat dst.txt,内容与src.txt完全一致,说明文件拷贝成功。需求:实现一个简单的服务端(Server)- 客户端(Client)通信模型,服务端持续监听命名管道,客户端向服务端发送消息,服务端实时打印客户端发送的内容,支持客户端退出后服务端继续等待新的客户端连接,这是命名管道在实际开发中的典型应用场景。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
#define FIFO_NAME "./server_fifo"
int main()
{
// 1. 创建命名管道,服务端作为管道的创建者
umask(0);
if (mkfifo(FIFO_NAME, 0644) < 0)
{
if (errno != EEXIST)
ERR_EXIT("mkfifo error");
}
printf("服务端启动,等待客户端连接...\n");
while (1)
{
// 2. 以阻塞读方式打开管道,客户端退出后,重新打开等待新客户端
int rfd = open(FIFO_NAME, O_RDONLY);
if (rfd < 0)
ERR_EXIT("open read error");
char buf[1024] = {0};
while (1)
{
ssize_t s = read(rfd, buf, sizeof(buf)-1);
if (s > 0)
{
buf[s-1] = 0; // 去掉换行符
printf("客户端消息:%s\n", buf);
}
else if (s == 0)
{
// 客户端关闭写端,服务端关闭当前读端,重新等待
printf("客户端断开连接,等待新客户端...\n");
close(rfd);
break;
}
else
{
ERR_EXIT("read error");
close(rfd);
break;
}
}
}
// 实际不会执行到这里,服务端持续运行
close(rfd);
unlink(FIFO_NAME);
return 0;
}#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
#define FIFO_NAME "./server_fifo"
int main()
{
// 1. 以阻塞写方式打开命名管道,连接服务端
int wfd = open(FIFO_NAME, O_WRONLY);
if (wfd < 0)
ERR_EXIT("open write error");
printf("客户端成功连接服务端,请输入消息(输入quit退出):\n");
// 2. 向服务端发送消息
char buf[1024] = {0};
while (1)
{
printf("> ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if (s > 0)
{
write(wfd, buf, s);
if (strncmp(buf, "quit", 4) == 0)
{
printf("客户端退出\n");
break;
}
memset(buf, 0, sizeof(buf));
}
}
// 3. 关闭写端
close(wfd);
return 0;
}编写 Makefile,方便编译:
.PHONY:all
all:serverPipe clientPipe
serverPipe:serverPipe.c
gcc -o $@ $^
clientPipe:clientPipe.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f serverPipe clientPipe编译:make;
运行服务端:./serverPipe,服务端启动并等待客户端连接;
运行客户端:打开新终端,./clientPipe,客户端成功连接服务端;
测试:客户端输入任意消息,服务端实时打印;客户端输入quit,断开连接,服务端继续等待新的客户端;
多客户端测试:打开多个终端,运行多个./clientPipe,依次向服务端发送消息,服务端均可正常接收。
结合前面的原理和实战,我们总结命名管道的核心特点,并梳理其典型的使用场景,帮助你在实际开发中快速判断是否适合使用命名管道。
open()/read()/write()/close()等标准文件操作接口,契合 “一切皆文件” 思想,学习成本低;命名管道适用于简单、低并发、单向 / 双向字节流通信的场景,尤其是需要在无亲缘进程间传递数据的场景,典型应用包括:
命名管道并非万能的,也存在一些局限性,在高并发、高要求的通信场景中,需要选择其他 IPC 方式:
为了方便在实际开发中选择合适的 IPC 方式,我们将命名管道与常见的 IPC 方式(匿名管道、共享内存、消息队列、Socket)进行核心对比:
IPC 方式 | 亲缘限制 | 通信方向 | 数据格式 | 跨主机 | 并发性能 | 核心优势 |
|---|---|---|---|---|---|---|
匿名管道 | 有 | 半双工 | 字节流 | 否 | 一般 | 简单、轻量、亲缘进程专用 |
命名管道 | 无 | 半双工 | 字节流 | 否 | 一般 | 无亲缘限制、接口统一 |
共享内存 | 无 | 全双工 | 自定义 | 否 | 极高 | 最快的 IPC 方式 |
消息队列 | 无 | 全双工 | 消息 | 否 | 较好 | 有消息边界、自带格式 |
Socket(本地) | 无 | 全双工 | 字节流 / 数据包 | 是 | 较好 | 跨主机、功能强大 |
选择建议:
在命名管道的实际开发中,新手很容易踩坑,导致程序出现阻塞、崩溃、数据丢失等问题。本节梳理开发中最常见的坑,并给出对应的解决方案,帮助你避开陷阱,写出健壮的代码。
问题:所有读端关闭后,写端继续写入数据,内核会发送 SIGPIPE 信号,默认处理方式是终止进程,导致程序意外退出。
解决方案:在写端程序中,捕获并忽略 SIGPIPE 信号:
#include <signal.h>
// 忽略SIGPIPE信号
signal(SIGPIPE, SIG_IGN); 问题:非阻塞模式下,read()/write()返回 - 1 时,直接认为是程序错误,实际上可能是EAGAIN(资源暂时不可用),属于正常情况。
解决方案:判断返回值为 - 1 时,结合errno进行判断,忽略EAGAIN:
ssize_t s = read(rfd, buf, sizeof(buf));
if (s < 0)
{
if (errno == EAGAIN)
{
// 非阻塞模式下,无数据,可重试
continue;
}
else
{
// 真正的错误,处理
perror("read error");
break;
}
}问题:多个进程同时向命名管道写入数据,且写入的数据量大于 PIPE_BUF,导致数据交错,出现混乱。
解决方案:保证每个进程的写入数据量不大于 PIPE_BUF,利用原子写入特性,避免数据交错;如果需要写入大数据,可将数据拆分为多个 PIPE_BUF 大小的块,依次写入。
问题:实现双向通信时,只创建一个命名管道,两个进程同时以读 + 写方式打开,导致双方相互阻塞,出现死锁。
解决方案:双向通信必须创建两个命名管道,分别负责 A→B 和 B→A 的数据流,每个进程对一个管道读,对另一个管道写。
问题:程序退出后,未删除 FIFO 文件,再次启动程序时,mkfifo()会因文件已存在而报错(errno=EEXIST)。
解决方案:
mkfifo()后,判断 errno 是否为 EEXIST,若是则忽略错误;unlink()函数删除 FIFO 文件; 问题:阻塞模式下,进程打开管道后,另一端一直未打开,导致进程卡死在open()调用,无法退出。
解决方案:
alarm()设置定时器,超时后触发 SIGALRM 信号,退出阻塞。命名管道的学习,不仅让我们掌握了一种实用的 IPC 方式,更让我们加深了对 Linux “一切皆文件” 设计思想的理解。在实际开发中,命名管道虽然不如 Socket 功能强大,不如共享内存速度快,但它以轻量、简单、易用的特点,在本地跨进程通信的场景中占据着重要的位置。 当然,命名管道也只是 Linux IPC 家族的一员,后续还可以继续学习共享内存(最快的 IPC)、消息队列(有消息边界的通信)、Socket(跨主机通信)等方式,构建完整的 Linux 进程间通信知识体系。但无论学习哪种 IPC 方式,命名管道的基础知识和设计思想,都是重要的铺垫。 希望本文能帮助你彻底吃透命名管道,在实际开发中灵活运用,实现高效的进程间通信!