关闭文件描述符的危险

关闭文件描述符的危险

在 Stack Overflow 上的问题从 bash 脚本检查程序是否存在接受的答案注意到关闭 stderr 是危险的:

(小注释:有些人会认为 2>&- 与 2>/dev/null 相同但更短 - 但事实并非如此。2>&- 关闭 FD 2,当它尝试写入 stderr 时会导致程序出错,这与成功写入并丢弃输出非常不同(并且很危险!))

  • 除了写入程序出现错误之外,还可能发生哪些危险的事情?
  • 使用 2>&- 是否可能会破坏写入程序(即停止其执行并且不允许清理)?

答案1

是的,它会以令人兴奋和有趣的方式打破现状。

问题在于类 Unix 系统通常按顺序分配文件描述符。当程序需要新的文件描述符时(即它们调用open()socket()以及分配 fd 的任何其他函数),内核将找到编号最小的“空闲”描述符,并将其分发出去。

现在想象一下,如果您愿意的话,您已经关闭了 fd 2 (stderr)。然后进程中的某个东西需要一个文件描述符(下一个打开的文件)。内核开始寻找一个空闲的 fd,发现 fd 2 未被使用,然后将其返回给程序。

现在,假设程序中的其他程序想要写入 stderr。它会盲目地写入 fd 2,因为 stderr 就在那里。但现在它没有。如果你幸运的,fd 2 以只读方式打开,写入出现错误。每个人都认为写入 stderr 总会成功,所以这很有趣。不过,至少同样可能的是,fd 是读写的(六 (2) 种模式中有五种是可fopen写的—— 、、、r+或),而原本应该发送到 stderr 的消息最终溅入了aa+ww+谁知道在哪里

更令人兴奋的是,*文件描述符是继承的fork()。这意味着每一个子进程还能够在意想不到的地方乱写。更糟糕的是,即使是“安全”的分叉策略(例如在之前关闭所有 fds)exec,通常也不会触及 fds 0、1 或 2。因此,您的小故障几乎肯定会经受住旨在防止跨进程边界灾难的普通策略的考验。

你可能会说,“好吧,那我就永远不会使用写入 stderr 的库了,而且我会小心,永远不会自己写入 stderr”。对此,我只想说两点:

  1. 将来你就不会那么小心了。你会忘记你关闭了 stderr。
  2. 每个人别的那些不幸处理你疯狂代码的人也不会那么小心。

朋友不让朋友关闭 stderr。Womble 出去。 放下麦克风

答案2

这可能非常糟糕,并导致未定义的行为。查看我的旧问题:MySQL 复制在 binlog 轮换后关闭

我们关闭了 mysql 守护进程的 fd 2,并将帮助消息写入 binlog.info。这破坏了主从复制。

相关内容