匿名管道
管道是 SylixOS 进程间通信的一种方式。和现实世界的传输管道类似,管道有两个端口:读端和写端,并且只允许数据从写端流向读端,所以管道是一种流式设备。
管道分为匿名管道 pipe 和命名管道 fifo。
创建一个匿名管道使用 pipe 函数,pipe 函数的输出参数为两个文件描述符:一个为读端文件描述符,一个为写端文件描述符。创建匿名管道后通常使用 posix_spawn 族或 spawn 族函数创建一个子进程,由于子进程继承了父进程的文件描述符,所以子进程和父进程均能使用 read 和 write 函数对匿名管道进行读和写操作。
虽然子进程和父进程均有匿名管道的两个文件描述符:读端文件描述符和写端文件描述符。但并不意味着匿名管道能进行父子进程间的全双工通信。匿名管道只有两端口:读端和写端,并且只允许数据从写端流向读端,所以匿名管道只能进行半双工通信。如果需要全双工通信,则需要创建两个匿名管道。
匿名管道操作
匿名管道的创建
#include <unistd.h>
int pipe(int iFd[2]);
int pipe2(int iFd[2], int iFlag);
以上函数原型分析:
- 以上函数成功时返回 0,失败时返回-1 并设置错误号。
- 输出参数 iFd 用于记录匿名管道的两个文件描述符: iFd[0] 为读端文件描述符,iFd[1] 为写端文件描述符。
- 参数 iFlag 是匿名管道的文件标志,可以使用 0 或以下的宏组合。
宏名 | 含义 |
---|---|
O_NONBLOCK | 读写管道是一个非阻塞操作 |
O_CLOEXEC | 当 exec 发生时,管道将被关闭 |
匿名管道的读写
由于 pipe 函数的输出参数为两个文件描述符:第一个为读端文件描述符,第二个为写端文件描述符。所以匿名管道的读和写操作可以使用标准的文件读写函数—— read 函数和 write 函数。
匿名管道的等待
在匿名管道为空或满时,读或写匿名管道操作将被阻塞(除非使用 pipe2 函数创建匿名管道时参数 iFlag 指定了 O_NONBLOCK 选项),而等待一个匿名管道可读或可写,可以调用 select 函数。
匿名管道的关闭
关闭一个匿名管道可以使用标准的文件关闭函数—— close 函数,由于匿名管道存在两个文件描述符,所以关闭一个匿名管道时必须使用 close 函数关闭匿名管道的两个文件描述符。
同时,由于子进程(如果存在)继承了匿名管道的两个文件描述符,所以子进程也需要关闭匿名管道的两个文件描述符。
下面程序展示了匿名管道的使用方法,父进程调用 posix_spawn 函数创建子进程,关闭子进程的标准输入和子进程的管道写端,并将管道的读端文件描述符复制到子进程的标准输入。子进程每隔 1 秒从管道的读端读取数据。
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
char buf[64] = {0};
int i;
for (i = 0; i < 10; i++) {
read(STDIN_FILENO, buf, sizeof(buf));
fprintf(stdout, "buf:%s\n", buf);
sleep(1);
}
close(STDIN_FILENO);
return (0);
}
#include <stdio.h>
#include <spawn.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>
#define SEND_STR "From parent."
int main (int argc, char *argv[])
{
posix_spawn_file_actions_t file_actions;
posix_spawnattr_t spawnattr;
pid_t pid;
int fd[2];
int ret, i;
char *cmd[] = {"child_process", (char *)0};
ret = pipe(fd);
if (ret < 0) {
fprintf(stderr, "pipe error.\n");
return (-1);
}
/*
* 初始化进程属性对象
*/
if (posix_spawnattr_init(&spawnattr) != 0) {
fprintf(stderr, "init posix_spawnattr_t failed\n");
return (-1);
}
/*
* 初始化文件操作对象
*/
if (posix_spawn_file_actions_init(&file_actions) != 0) {
fprintf(stderr, "init posix_spawn_file_actions_t failed\n");
return (-2);
}
/*
* 关闭子进程的标准输入和管道的写端
*/
posix_spawn_file_actions_addclose(&file_actions, STDIN_FILENO);
posix_spawn_file_actions_addclose(&file_actions, fd[1]);
/*
* 将读端复制到子进程的标准输入
*/
posix_spawn_file_actions_adddup2(&file_actions, fd[0], STDIN_FILENO);
if (posix_spawn(&pid, "./child_process",
&file_actions, &spawnattr, cmd, NULL) != 0) {
posix_spawnattr_destroy(&spawnattr);
posix_spawn_file_actions_destroy(&file_actions);
return (-6);
}
close(fd[0]);
for (i = 0; i < 10; i++) {
write(fd[1], SEND_STR, strlen(SEND_STR));
sleep(1);
}
close(fd[1]);
wait(NULL);
posix_spawnattr_destroy(&spawnattr);
posix_spawn_file_actions_destroy(&file_actions);
return (0);
}