1 有名管道介绍
有名管道(Named Pipe),也称为 FIFO(First In First Out),是 Linux 进程间通信(IPC)的一种方式。FIFO 不同于管道之处在于它提供一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中。这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信(能够访问该路径的进程以及 FIFO 的创建进程之间),因此,通过 FIFO 不相关的进程也能交换数据。
它与匿名管道的核心区别在于:
-
匿名管道 仅适用于 有亲缘关系的进程(如父子进程),而 有名管道 允许 任意进程(即使无亲缘关系)通过文件系统路径进行通信。
-
有名管道 在文件系统中有一个 持久化的节点(类似于文件),而匿名管道是临时的,仅存在于内存中。
2 shell 编程应用
- 打开一个 终端A 发送信息
# 创建命名管道
mkfifo mypipe
# 进程 A 写入数据
echo "Hello from Process A" > mypipe
- 打开一个终端B 接收信息
# 进程 B 读取数据
cat mypipe
- 在终端A或者终端B中要删除有名管道,需要手动删除
rm mypipe
3 进程中有名管道用到函数
3.1 mkfifo 函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mkfifo(const char * pathname, mode_t mode)
- 其中输入形参:
pathname:FIFO 文件的路径(如 “/tmp/myfifo”)。
mode:权限模式(如 0666,表示所有用户可读写), mkfifo()建立的 FIFO 文件其他进程都可以用读写一般文件的方式存取。
具体案例如下:
mkfifo("/tmp/myfifo", S_IFIFO | 0666); // S_IFIFO 可省略,因 mkfifo 已隐含
- 返回值:
成功返回 0,失败返回 -1 并设置 errno。 - mkfifo()会依参数 pathname 建立特殊的 FIFO 文件,该文件必须不存在。
3.2 删除函数
- 可以利用停止信号,来触发删除函数,否则需要手动删除
unlink(const char * pathname); // 删除 FIFO 文件
- 有可能存在残留的情况,最好程序启动的时候,删除残留。
if (access("/tmp/myfifo", F_OK) == 0) {unlink("/tmp/myfifo");
}
3.3 其它文件操作函数说明
- 只读方式打开,默认阻塞模式
int fd = open("/tmp/myfifo", O_RDONLY ); // 阻塞模式打开
- 只读方式打开,采用非阻塞方式
int fd = open("/tmp/myfifo", O_RDONLY | O_NONBLOCK); // 非阻塞模式打开
- 以只写方式打开,默认采用阻塞模式
int fd = open("/tmp/myfifo", O_WRONLY ); // 阻塞模式打开
如果没有写入端,open() 会 立即返回 -1,并设置 errno = ENXIO。
3.4 阻塞与非阻塞模式说明
3.5 注意事项
-
需要轮询(polling)的 IPC 通信。
-
避免死锁(如双向通信时双方都在等待对方)。
-
如果需要双工通讯,需要创建两个管道。
3.6 总结
4 代码演示
4.1 单工通讯
4.1.1 发送端
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>#define FIFO_PATH "/tmp/myfifo"int main() {// 创建 FIFO(如果不存在)if (mkfifo(FIFO_PATH, 0666) == -1) {perror("mkfifo failed");// 如果已存在,继续使用(不报错)}// 打开 FIFO(阻塞模式,等待读取端连接)int fd = open(FIFO_PATH, O_WRONLY);if (fd == -1) {perror("open failed");exit(EXIT_FAILURE);}// 写入数据char *msg = "Hello from writer!";if (write(fd, msg, strlen(msg) + 1) == -1) {perror("write failed");} else {printf("Writer sent: %s\n", msg);}close(fd);return 0;
}
4.1.2 接收端
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>#define FIFO_PATH "/tmp/myfifo"int main() {// 创建 FIFO(如果不存在)if (mkfifo(FIFO_PATH, 0666) == -1) {perror("mkfifo failed");// 如果已存在,继续使用(不报错)}// 打开 FIFO(阻塞模式,等待写入端连接)int fd = open(FIFO_PATH, O_RDONLY);if (fd == -1) {perror("open failed");exit(EXIT_FAILURE);}// 读取数据char buf[100];ssize_t bytes_read = read(fd, buf, sizeof(buf));if (bytes_read == -1) {perror("read failed");} else {printf("Reader received: %s\n", buf);}close(fd);// 程序退出后删除 FIFO(可选)unlink(FIFO_PATH);return 0;
}
4.2 双工通讯
下面用server 发送,client 接收实现全双工通讯。
4.2.1 服务器代码
server.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define FIFO_READ "readfifo"
#define FIFO_WRITE "writefifo"
#define BUF_SIZE 1024
int main(void)
{int wfd,rfd;char buf[BUF_SIZE];int len;umask(0);if(mkfifo(FIFO_WRITE,S_IFIFO|0666)){printf("can't create FIFO %s beause %s",FIFO_WRITE,strerror(errno));exit(1);}umask(0);
wfd = open(FIFO_WRITE,O_WRONLY);
if(wfd == -1){
printf("open FIFO %s error :%s",FIFO_WRITE,strerror(errno));
exit(1);
}
while((rfd = open(FIFO_READ,O_RDONLY)) == -1){
sleep(1);
}
while(1) {
printf("Server:");
fgets(buf,BUF_SIZE,stdin);
buf[strlen(buf)-1] = '\0';
if(strncmp(buf,"quit",4) == 0) {
close(wfd);
unlink(FIFO_WRITE);
close(rfd);
exit(0);
}
write(wfd,buf,strlen(buf));
len = read(rfd,buf,BUF_SIZE);
if(len > 0) {
buf[len] = '\0';
printf("Client:%s\n",buf);
}
}
}
4.2.2 用户程序
linux中的 umask 函数主要用于:
在创建新文件或目录时,屏蔽掉新文件或目录不应有的访问允许权限。文件的访问允许权限共有9种,分别是:r w x r w x r w x(它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行)。
其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0666 & mode了,这样就是给后面的代码调用函数mkfifo给出最大的权限,避免了创建目录或文件的权限不确定性。**
**S_IFIFO|0666”指明创建一个命名管道且存取权限为0666,即创建者、与创建者同组的用户、其他用户对该命名管道的访问权限都是可读可写(这里要注意umask对生成的管道文件权限的影响)。
client.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define FIFO_READ "writefifo"
#define FIFO_WRITE "readfifo"
#define BUF_SIZE 1024
int main(void)
{int wfd,rfd;char buf[BUF_SIZE];int len;umask(0);if(mkfifo(FIFO_WRITE,S_IFIFO|0666)){printf("can't create FIFO %s beause %s",FIFO_WRITE,strerror(errno));exit(1);}while((rfd = open(FIFO_READ,O_RDONLY)) == -1) {sleep(1);}Wfd = open(FIFO_WRITE,O_WRONLY);if(wfd == -1){printf("open FIFO %s error :%s",FIFO_WRITE,strerror(errno));exit(1);}while(1){len = read(rfd,buf,BUF_SIZE);if(len > 0) {buf[len] = '\0';printf("Server:%s\n",buf);}printf("Client:");fgets(buf,BUF_SIZE,stdin);buf[strlen(buf)-1] = '\0';if(strncmp(buf,"quit",4) == 0) {close(wfd);unlink(FIFO_WRITE);close(rfd);exit(0);}write(wfd,buf,strlen(buf));}return 0;
}