Linux 进程间通信--命名管道
七月 20, 2025
次阅读
mkfifo 命令
mkfifo 是 Linux/Unix 系统中用于创建命名管道(named pipe,也称为 FIFO)的命令。命名管道是一种特殊的文件类型,允许不相关的进程通过文件系统进行通信。
基本语法
mkfifo [选项] 管道名
主要选项
-m MODE或--mode=MODE:设置管道的权限模式(类似 chmod)-Z:设置 SELinux 安全上下文(在 SELinux 启用的系统上)
使用示例
1. 创建基本命名管道
mkfifo mypipe
这会在当前目录创建一个名为 “mypipe” 的命名管道。
2. 创建带有特定权限的管道
mkfifo -m 644 mypipe
这会创建一个权限为 644(所有者可读写,其他人只读)的命名管道。
3. 使用命名管道进行进程通信
在终端1中:
cat > mypipe
在终端2中:
cat < mypipe
此时在终端1中输入的内容会出现在终端2中。
4. 结合其他命令使用
mkfifo mypipe
tar -czf mypipe directory/ &
tar -xzf mypipe -C destination/
这通过命名管道将一个目录压缩并直接解压到另一个位置,而不生成中间文件。
工作原理
命名管道与匿名管道(|)类似,但有几点关键区别:
- 命名管道在文件系统中有一个名称,不相关的进程可以通过这个名称访问它
- 命名管道会一直存在,直到被显式删除(使用
rm) - 读写操作是阻塞的 - 读操作会等待有数据写入,写操作会等待有进程开始读取
实际应用场景
- 进程间通信:允许不相关的进程交换数据
- 日志处理:多个进程可以向同一个管道写入日志,由一个中心进程处理
- 流处理:在不创建临时文件的情况下传递大量数据
- 网络编程:有时用于本地客户端和服务器之间的通信
注意事项
- 命名管道不存储数据 - 它只是连接读写进程的通道
- 如果所有写端关闭,读端会收到 EOF
- 如果没有读端,写操作会阻塞直到有读端打开管道
- 命名管道可以多次打开,允许多个读写者
mkfifo 函数
mkfifo是C语言中用于创建命名管道(FIFO)的系统函数,它是在POSIX标准中定义的,可以在<sys/stat.h>头文件中找到。
函数原型
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数说明
- pathname:要创建的FIFO文件的路径名
- mode:指定FIFO的权限模式,通常使用八进制表示(如0666)
返回值
- 成功时返回0
- 失败时返回-1,并设置errno来指示错误
常见错误码(errno)
EACCES:路径中的某个目录不允许搜索(执行)权限EEXIST:指定的pathname已经存在ENAMETOOLONG:路径名过长ENOENT:路径中的某个目录不存在ENOSPC:包含该文件的设备没有空间了ENOTDIR:路径中的某个部分不是目录EROFS:指定的文件位于只读文件系统上
基本用法示例
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.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("FIFO created at %s\n", fifo_path);
// 这里可以添加对FIFO的读写操作...
// 使用完后删除FIFO
unlink(fifo_path);
return 0;
}
FIFO的读写操作
创建FIFO后,可以使用标准文件I/O函数进行读写:
写入示例
int fd = open(fifo_path, O_WRONLY);
write(fd, "Hello, FIFO!", 12);
close(fd);
读取示例
int fd = open(fifo_path, O_RDONLY);
char buf[256];
read(fd, buf, sizeof(buf));
close(fd);
重要特性
阻塞行为:
- 打开一个FIFO进行读取(
O_RDONLY)会阻塞,直到有进程打开同一个FIFO进行写入 - 打开一个FIFO进行写入(
O_WRONLY)会阻塞,直到有进程打开同一个FIFO进行读取
- 打开一个FIFO进行读取(
非阻塞模式:
- 可以通过
O_NONBLOCK标志改变阻塞行为
- 可以通过
int fd = open(fifo_path, O_RDONLY | O_NONBLOCK);
- 数据传递:
- FIFO中的数据是字节流,没有消息边界
- 多个进程可以同时写入同一个FIFO,但这样可能会导致数据交错
注意事项
- FIFO在文件系统中有一个名称,但不会实际存储数据
- 当所有写端关闭后,读端会读到EOF
- 如果没有读端,写操作会阻塞(除非使用非阻塞模式)
- FIFO的数据是先进先出的,读取顺序与写入顺序一致
- 使用完毕后应该删除FIFO文件
应用示例
下面通过 mkfifo 函数创建一个命名管道用来实现两个无亲属关系的进程之间的通信
// common.hpp
#ifndef _COMMON_HPP_
#define _COMMON_HPP_ 1
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
const char *file_name = "myfifo";
const int mode = 0664;
const int MAX_SIZE = 1024;
enum global_error{
mkfifo_error = 1,
unlink_error,
write_error,
read_error,
open_error,
};
// 该函数用于初始化命名管道以及 RAII 清理命名管道
class InitPipe{
public:
InitPipe()
{
if(mkfifo(file_name, mode) == -1)
{
perror("InitPipe(): mkfifo error");
exit(global_error::mkfifo_error);
}
}
~InitPipe()
{
if(unlink(file_name) == -1)
{
perror("~InitPipe(): unlink error");
exit(global_error::unlink_error);
}
}
};
#endif
服务端代码
// server.cc
#include "common.hpp"
using namespace std;
int main()
{
InitPipe init;
int fd = open(file_name, O_RDONLY);
if(fd == -1)
{
perror("server.o: open error");
exit(global_error::open_error);
}
char inbuffer[MAX_SIZE];
while(true)
{
int n = read(fd, inbuffer, MAX_SIZE - 1);
if(n == -1)
{
perror("server.o: read error");
exit(global_error::read_error);
}
inbuffer[n] = 0;
if(strcmp(inbuffer, "quit") == 0)
{
break;
}
cout << "server get information: " << inbuffer << endl;
}
return 0;
}
客户端代码
#include "common.hpp"
using namespace std;
int main()
{
int fd = open(file_name, O_WRONLY);
string outbuffer;
while(true)
{
cout << "Please Enter: ";
cin >> outbuffer;
int n = write(fd, outbuffer.c_str(), outbuffer.size());
if(n == -1)
{
perror("client.o: write error");
exit(global_error::write_error);
}
if(outbuffer == "quit")
{
cout << "bye" << endl;
return 0;
}
}
return 0;
}
可以观察到,数据被正常接受处理:
# client
╭─ljx@VM-16-15-debian ~/linux_review/fifo
╰─➤ ./client.o
Please Enter: 123
Please Enter: 321
Please Enter: 135
Please Enter: quit
bye
#server
╭─ljx@VM-16-15-debian ~/linux_review/fifo
╰─➤ ./server.o
server get information: 123
server get information: 321
server get information: 135
查看评论