【问题标题】:Usage of dup2()dup2() 的用法
【发布时间】:2017-09-13 20:19:24
【问题描述】:
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2)
  close(fd);

根据《UNIX 环境下的高级编程》一书,上面的 if 语句是必要的。这本书建议通过考虑如果 fd = 1 在一种情况下会发生什么来解决它,如果 fd = 3 在另一种情况下会发生什么。

如果 fd = 1, 0 (stdin) 将被关闭,然后将指向 stdout,1 仍将指向 stdout(因为 dup2() 不会关闭文件描述符,如果它等于第一个参数,并且会return 1) 和 2 将指向标准输出。

如果 fd = 3,每个文件描述符将首先关闭,然后指向 3 指向的任何文件。

大于2的描述符为什么要关闭?

【问题讨论】:

  • 通过执行这一系列的 fd 操作,您正试图达到某种目标状态。目标状态是否包括更高 fd 上的 stdin/stdout/stderr 的额外副本?
  • @WumpusQ.Wumbley 除了问题中的代码 sn-p 以及为什么需要 if 语句的问题之外,该问题没有其他上下文。
  • 你认为有任何上下文,其中一个合理的目标状态是“有时,有一个额外的文件描述符,取决于我们开始的文件描述符编号”?
  • 在重复描述符 3 次之后,似乎没有额外的描述符成为一个特别紧迫的问题,所以我认为肯定还有其他问题在起作用 - 到最后,我们有 4 个描述符都指向同一个文件表条目,关闭原始有什么好处,因为仍然存在冗余?
  • @bag 我已经解释了为什么关闭未使用的描述符对我的答案很重要。

标签: linux unix gnu file-descriptor


【解决方案1】:

您不想让任何未使用的文件描述符保持打开状态。将stdinstdoutstderr 重定向到fd 后,您不打算单独使用fd - 它只是暂时打开,以便您可以将所有标准描述符复制到它。所以你需要关闭它。

但如果它实际上与stdinstdoutstderr 相同,则您不想关闭它(这应该不太可能,但如果其中一个在您打开之前已经关闭,则有可能@ 987654329@)。所以你需要if 来防止这种情况发生。

关于为什么关闭原始描述符很重要,某些类型的流只有在所有引用它的描述符都关闭时才会做一些特殊的事情(而且通常很重要)。例如,一个 TCP 连接在所有描述符都关闭之前不会关闭,一个被删除的文件只有在所有链接都被删除并且所有描述符都关闭时才会从磁盘中删除它的数据,并且管道的读取端只有在以下情况下才会收到 EOF所有引用写结束的描述符都被关闭。因此,如果您将 fd 保持打开状态,然后关闭标准描述符,它将阻止此最终操作发生。

需要关闭描述符的一个非常常见的情况是创建管道以与子进程通信时。代码通常看起来像:

pipe(fds);
switch (fork()) {
    case 0: // child
        dup2(fds[0], STDIN_FILENO);
        close(fds[0]);
        close(fds[1]);
        execlp("program", "program", "arg1", (char*)NULL);
        break;
    case -1: // error
        perror("fork");
        exit(-1);
        break;
    default: // parent
        close(fds[0]);
        // Code that writes to fds[1]
}

孩子需要关闭管道的写入端(fds[1]),否则当父母关闭时孩子不会收到EOF。

【讨论】:

  • 这是一个关于关闭原始描述符的有趣(并且非常有见地,谢谢!)细节,但是,书中没有提到它,所以我倾向于认为它可能不是这里的答案。
  • 书中提到sn-p这个代码是很常见的,所以进程fork的例子可能就是答案。
  • @bag Forking 通常不会复制所有 3 个描述符。您示例中的模式在xinetd 等通用网络服务器中很常见。
【解决方案2】:

dup2 调用的目的是将第一个文件描述符复制到第二个。所以在对dup2 的三个调用之后,文件描述符 0、1 和 2 是打开的,并且是文件描述符 fd 的副本。随后对close 的调用将关闭原始文件描述符。

如果您在调用close(fd) 之前没有检查fd > 2,那么您将关闭您刚刚打开的文件描述符之一。例如,如果 fd 为 2,则 close(fd)close(2) 相同。

打开文件描述符后立即关闭它并没有对其进行任何操作。这就是为什么检查是必要的。

【讨论】:

  • dup2() 不会将第二个参数复制到第一个参数吗?啊,好吧,我已经明白了这么多,但我认为这个问题有一些我不理解的更复杂的东西,因为代码没有真正的上下文。
  • 不,dup2() 将第一个参数处理为第二个参数。唯一要考虑的另一件事是通常 stdin/out/err 已经打开,这意味着如果 fd 是例如结果打开一个文件,它的值将永远不会 fd 传递,或者例如关闭标准输入(即描述符 0),然后打开一个文件 - 在这种情况下,fd 中的描述符将被赋予值 0,因为如果标准输入已关闭,这是可用的最低描述符。
【解决方案3】:

重点是fd大于2就不要关闭;相反,如果它是标准流之一,则不要关闭fd。在您的示例中,关闭 1 (stdout) 显然是不正确的。

但是额外的文件描述符确实需要关闭。当一个可执行文件启动时,它有权相信只有 fds 0、1 和 2 在使用,因此在execing 之前清理 -- 关闭 -- fds 很重要。 close-on-exec 标志是另一个有用的选项。

【讨论】:

  • 啊,在使用 dup2() 之后,第二个参数的 close-on-exec 标志被清除了,对吧?这就是为什么我们应该明确关闭它?
猜你喜欢
  • 1970-01-01
  • 2011-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-24
  • 2017-09-05
相关资源
最近更新 更多