功能类似于 pipe(3) 但 returns FILE *

Function like pipe(3) but returns FILE *

是否有类似 int pipe(int pipefd[2]) 但 returns FILE 指针的函数?我知道我可以 FILE * fdopen(int) 两个文件描述符而且我知道当我 int fclose(FILE *) FILE 指针时它会关闭底层文件描述符所以我不需要跟踪它们, 但最好完全坚持使用文件描述符或 FILE 指针。

int fpipe(FILE * pipes[2]) {
  int result; 
  int pipefd[2];
  FILE * pipe[2];

  if (0 != (result = pipe(pipefd))) {
    return result;
  }

  if (NULL == (pipe[0] = fdopen(pipefd[0], "r"))) {
    close(pipefd[0]);
    close(pipefd[1]);
    return errno;
  }

  if (NULL == (pipe[1] = fdopen(pipefd[1], "w"))) {
    fclose(pipe[0]);
    close(pipefd[1]);
    return errno;
  }

  pipes[1] = pipe[1];
  pipes[0] = pipe[0];
  return 0;
}

您的 fpipe() 函数已接近所需,但没有标准函数可以完成相同的工作,因此您需要编写一些东西。没有这样一个标准函数的主要原因是大多数情况下你最终会分叉然后使用 dup()dup2() 最后一个 exec*() 函数,所以真的没有太多大部分时间都受益于文件流。

如评论中所述,您需要决定 return 成功和出错的内容,并相应地管理 errno。有两种合理的设计(以及两者的先例):

  1. 函数 returns 0 成功,-1 失败,在 errno 变量中有更详细的错误信息(经典函数调用技术:见 open(), close(), read(), write(), …).
  2. 函数 returns 0 成功,错误编号(不修改 errno)——这是 POSIX 线程使用的技术(pthreads) 函数。

两种设计都会在出错时使 pipes[] 数组处于不确定状态。这不是没有道理的;在调用 fpipe() 之前,参数数组不应指向有价值的文件流,因为如果调用成功,值将丢失。

请记住,没有标准 C 或 POSIX 库函数会将 errno 设置为零。 (参见 POSIX errno)。

这里有两种设计,根据编译器命令行中有无-DUSE_PTHREAD_COMPATIBLE_DESIGN选择:

#include <errno.h>
#include <stdio.h>
#include <unistd.h>

extern int fpipe(FILE *pipes[2]);  // Should be in a header

#ifndef USE_PTHREAD_COMPATIBLE_DESIGN
// Design 1 - return -1 on failure and set errno
int fpipe(FILE *pipes[2])
{
    int pipefd[2];

    if (pipe(pipefd) != 0)
        return -1;

    if ((pipes[0] = fdopen(pipefd[0], "r")) == NULL)
    {
        close(pipefd[0]);
        close(pipefd[1]);
        return -1;
    }

    if ((pipes[1] = fdopen(pipefd[1], "w")) == NULL)
    {
        fclose(pipes[0]);
        close(pipefd[1]);
        return -1;
    }

    return 0;
}

#else

// Design 2 - return error number on failure and don't modify errno
int fpipe(FILE *pipes[2])
{
    int saved_errno = errno;
    int rc = 0;
    int pipefd[2];

    if (pipe(pipefd)) != 0)
        rc = errno;
    else if ((pipes[0] = fdopen(pipefd[0], "r")) == NULL)
    {
        rc = errno;
        close(pipefd[0]);
        close(pipefd[1]);
    }
    else if ((pipes[1] = fdopen(pipefd[1], "w")) == NULL)
    {
        rc = errno;
        fclose(pipes[0]);
        close(pipefd[1]);
    }

    errno = saved_errno;
    return rc;
}

#endif /* USE_PTHREAD_COMPATIBLE_DESIGN */

在函数的第一个变体中,因为 if 块的主体总是以 return 结尾,所以下一个块不需要使用 else if。对于第二个变体,if 块不会 return 所以 else if 很重要。 C 测试赋值结果的能力在这里有很大的帮助。

如果您愿意,可以在第二个变体中的 return 之前添加 if (rc != 0) pipes[0] = pipes[1] = NULL;。您必须将这些分配放在其他设计中的更多位置。事实上,我可能会在输入时将值设置为 NULL,然后仅在 pipes[0] 已初始化且 pipes[1] 未初始化时才将其重置为 NULL。

pipe(2) 系统调用提供文件描述符(通过在不失败时填充其中两个的数组)。您应该针对 pipe.

的失败进行测试

fdopen(3) function produces a FILE* stream from a file descriptor.

您只需根据需要将两者结合起来即可。

还有一个popen(3)函数(你会用pclose代替fclose)。

一些系统(但不是 Linux)也有 p2open