匿名管道

更新时间:
2024-05-29
下载文档

匿名管道

管道是 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);
}
文档内容是否对您有所帮助?
有帮助
没帮助