管道是Unix/Linux系统中最基础的进程间通信机制,分为匿名管道和命名管道两种类型,都具有自同步和单工的特点。
匿名管道通过pipe()系统调用创建,只能用于有血缘关系的进程间通信,如父子进程或兄弟进程,它创建一个单向的单工数据传输通道;命名管道(FIFO)通过mkfifo()创建,以特殊文件形式存在于文件系统中,可以用于任意进程间的通信。
两种管道都具有天然的自同步特性:当管道缓冲区满时,写操作会自动阻塞直到有空间可用;当管道为空时,读操作会自动阻塞直到有数据可读,这种阻塞机制无需额外的同步代码即可实现发送方和接收方进程的自动协调。
管道的数据流动是单向的,发送方只能写入数据,接收方只能读取数据,体现了典型的单工通信特点,但管道容量有限且不支持随机访问。
匿名管道
函数pipe
int pipe(int pipefd[2]);
作用
pipe
用于创建一个管道,这是一个可以用于进程间通信的单向数据通道。数组 pipefd
被用来返回指向管道两端的两个文件描述符。pipefd[0]
指向管道的读端,pipefd[1]
指向管道的写端。
参数
int pipefd[2]
:一个包含两个元素的整型数组,用于返回指向管道两端的文件描述符。pipefd[0]
是读端,pipefd[1]
是写端。
返回值
- 成功时返回 0。
- 失败时返回 -1,并设置
errno
指示错误类型,且pipefd
数组内容不变。
函数pipe2(Linux)
int pipe2(int pipefd[2], int flags);
作用
pipe2
与 pipe
类似,但允许在创建管道时通过 flags
参数指定额外的选项。如果 flags
为 0,则 pipe2
的行为与 pipe
相同。
参数
int pipefd[2]
:一个包含两个元素的整型数组,用于返回指向管道两端的文件描述符。pipefd[0]
是读端,pipefd[1]
是写端。int flags
:可以是以下值的按位或组合:
标志 | 功能描述 | 主要用途 |
---|---|---|
O_CLOEXEC | 为新创建的两个文件描述符设置 close-on-exec 标志(FD_CLOEXEC) | 防止子进程意外继承管道 |
O_DIRECT | 创建一个以”包”模式进行 I/O 的管道,每次 write(2) 被视为独立包,read(2) 一次读取一个包 | 实现包模式的管道 I/O 操作 |
O_NONBLOCK | 为新文件描述符关联的打开文件描述设置 O_NONBLOCK 文件状态标志 | 避免后续调用 fcntl(2) 来设置非阻塞模式 |
O_NOTIFICATION_PIPE | 创建一个用于接收内核通知消息的管道,内核将通知消息拼接到管道中 | 用户空间接收内核通知消息 |
返回值
- 成功时返回 0。
- 失败时返回 -1,并设置
errno
指示错误类型,且pipefd
数组内容不变。
示例
#include "stdio.h"#include "stdlib.h"#include "sys/ipc.h"#include "unistd.h"#include <sys/types.h>#include "sys/wait.h"#define BUFFSIZE 1024int main() { int pd[2]; pid_t pid; char buf[BUFFSIZE]; if (pipe(pd) < 0) { perror("pipe()"); } pid = fork(); if (pid < 0) { perror("fork()"); exit(1); } if (pid == 0) { // 读管道 close(pd[1]); int len = read(pd[0], buf, BUFFSIZE); buf[len] = '\0'; printf("%s\n", buf); } else { // 写管道 close(pd[0]); write(pd[1], "Hello", 6); close(pd[1]); } wait(NULL); exit(0);}
FIFO(命名管道)
函数mkfifo
int mkfifo(const char *pathname, mode_t mode);
作用
mkfifo
用于创建一个FIFO特殊文件(命名管道)。FIFO以文件形式存在于文件系统中,可以被不相关的进程打开和使用。创建后,任何进程都可以像打开普通文件一样打开它进行读写,但必须同时打开读端和写端才能进行I/O操作。
参数
const char *pathname
:要创建的FIFO文件的路径名。mode_t mode
:指定FIFO文件的权限。它会按照常规方式被进程的umask修改,即创建文件的实际权限为(mode & ~umask)
。
返回值
- 成功时返回0。
- 失败时返回-1,并设置
errno
指示错误类型。
函数mkfifoat
int mkfifoat(int dirfd, const char *pathname, mode_t mode);
作用
mkfifoat
的功能与 mkfifo
相同,但提供了更灵活的路径解析方式。如果 pathname
是相对路径,则相对于 dirfd
文件描述符所指的目录进行解析;如果 pathname
是绝对路径,则 dirfd
被忽略。若 dirfd
为特殊值 AT_FDCWD
,则行为与 mkfifo
相同。
参数
int dirfd
:用于解析相对路径的目录文件描述符,或特殊值AT_FDCWD
。const char *pathname
:要创建的FIFO文件的路径名(可以是相对或绝对路径)。mode_t mode
:指定FIFO文件的权限,实际权限为(mode & ~umask)
。
返回值
- 成功时返回0。
- 失败时返回-1,并设置
errno
指示错误类型。
示例
fifo_r.c
#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <sys/stat.h>#include <unistd.h>
#define FIFO_NAME "./fifo"#define BUFFSIZE 1024int main() { int fd; fd = open(FIFO_NAME, O_RDONLY); if (fd < 0) { perror("open()"); exit(0); } char buf[BUFFSIZE];
int len = read(fd, buf, BUFFSIZE); buf[len] = '\0'; printf("%s\n", buf); close(fd); return 0;}
fifo_w.c
#include "string.h"#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <sys/stat.h>#include <unistd.h>
#define FIFO_NAME "./fifo"int main() { int fd; if (mkfifo(FIFO_NAME, 0666) == -1) { perror("mkfifo()"); exit(1); } fd = open(FIFO_NAME, O_WRONLY); if (fd < 0) { perror("open()"); exit(0); } char *messige = "Hello"; write(fd, messige, strlen(messige) + 1); close(fd); return 0;}
补充
命令 mkfifo
mkfifo
是一个用于创建命名管道(FIFO)的命令。命名管道是一种特殊类型的文件,用于进程之间进行通信。
mkfifo [选项] [名称]
主要功能:
- 创建一个或多个命名管道(FIFO)。
- 允许不同进程通过这些管道进行数据交换。
常用选项:
-m, --mode=MODE
:设置 FIFO 的权限模式,类似于chmod
命令的用法。
示例:
-
创建一个名为
myfifo
的命名管道:Terminal window mkfifo myfifo -
创建一个具有特定权限的命名管道:
Terminal window mkfifo -m 644 myfifo