【问题标题】:Closing opened file descriptors in child process关闭子进程中打开的文件描述符
【发布时间】:2013-02-22 09:01:56
【问题描述】:

有没有办法遍历已经打开的文件描述符(由父进程打开)并在子进程中一个一个地关闭它们?

操作系统:Unix。

关闭原因:setrlimit() 的 RLIMIT_NOFILE 限制限制了进程可以分配的文件描述符的数量。如果我们想通过设置这个限制来限制我们的子进程,这取决于已经分配的文件描述符。

尝试在子进程中设置此限制会受到限制,因为父进程有一些打开的文件描述符,因此我们不能将此限制设置为小于该数量。

示例:如果父进程分配了 10 个文件描述符,并且我们希望将子进程的文件描述符数限制为小于 10(例如 3),我们需要关闭子进程内的 7 个文件描述符。

解决方案可以使所有希望限制其子进程创建新文件或打开新网络连接的人受益。

【问题讨论】:

  • “如果父进程分配了 10 个文件描述符......”:“已分配”对您意味着什么?因为文件描述符要么打开要么关闭。字分配与内存管理相关...
  • 更正:我的意思是说 10 个打开的父进程的文件描述符,感谢更正thkala

标签: unix fork parent-child file-descriptor setrlimit


【解决方案1】:

以下成语并不少见(取自MIMEDefang的C部分):

/* Number of file descriptors to close when forking */
#define CLOSEFDS 256
...

static void
closefiles(void)
{
    int i;
    for (i=0; i<CLOSEFDS; i++) {
        (void) close(i);
   }
}

这是一种 hack(正如 MIMEDefang 代码所承认的那样)。在许多情况下,从 FD 3(或STDERR_FILENO+1)而不是 0 开始会更有用。close() 返回带有无效 FD 的 EBADF,但这通常不会出现问题(至少在 C 中不会,在其他语言可能会引发异常)。

由于您可以使用getrlimit(RLIMIT_NOFILE,...) 确定文件描述符上限,即defined

RLIMIT_NOFILE

这是一个大于系统可以分配给新创建的描述符的最大值的数字。如果超过此限制,分配文件描述符的函数将失败,并将 errno 设置为 [EMFILE]。这个限制限制了进程可以分配的文件描述符的数量。

你可以使用这个(减去 1)作为循环的上限。 以上和ulimit -ngetconf OPEN_MAXsysconf(OPEN_MAX)应该都同意。

由于open()总是分配最低的空闲FD,所以最大打开文件数和最高的FD+1是同一个数。

要确定哪些 fds 是打开的,而不是 close() 使用无操作 lseek(fd, 0, SEEK_CUR) 如果 fd 未打开,它将返回 EBADF(调用 lseek() 以获取条件 @987654338 没有明显的好处@ 尽管)。 socatfilan 循环超过 0 .. FD_SETSIZE 调用 fstat()/fstat64()

守护任意进程的libslack daemon utility 也使用这种蛮力方法(同时确保在inetd 下使用时保持前三个描述符打开)。

如果您的程序可以跟踪文件句柄,最好这样做,或者在可用的情况下使用FD_CLOEXEC。但是,如果您希望进行防御性编码,您可能更愿意不信任您的父进程,例如由浏览器启动的外部处理程序/查看器进程,例如像这样long-lived and ancient Mozilla bug 在 Unix 平台上。

对于偏执狂(您是否希望您的 PDF 查看器继承每个打开的 Firefox FD,包括您的缓存和打开的 TCP 连接?):

#!/bin/bash
# you might want to use the value of "ulimit -n" instead of picking 255
for ((fd=3; fd<=255; fd++)); do
  exec {fd}<&- # close
done
exec /usr/local/bin/xpdf "$@"

【讨论】:

    【解决方案2】:

    据我所知,在 Unix/POSIX 中没有通用的方法来迭代打开的文件描述符。处理您描述的问题的传统方法是在您自己的代码中跟踪它们,如果需要,使用数组或列表等数据结构,并在fork() 之后但@987654324 之前在子进程中关闭它们@。

    但是,如果您在创建子进程后调用exec(),某些操作系统会提供潜在的解决方案。通过setting the FD_CLOEXEC flag for a file descriptor using fcntl()O_CLOEXEC 标志open() 指示操作系统在调用exec() 之前关闭该特定文件描述符。您必须查阅目标操作系统的文档,以了解是否支持这些标志以及哪些标志受支持。

    【讨论】:

    • 我可以循环从 0 到 MAX ALLOWED 文件描述符的文件描述符编号,并根据 dup(int fildes) 函数的输出进行调用吗?由于 dup() 如果失败,则返回“-1”。这是一种不好的方式,还是另一种了解我正在寻找的信息的粗略方式?
    • @user2136293:您的提案中有 很多 的假设可以并且将会破坏您的代码: 1. 您假设您可以可靠地找出最大 FD数是。除了 MAX_INT 之外,没有其他可靠的限制。 2. 你假设这个数字是“合理的”。你要探测 2^31 个 FD 吗? 3.你假设你可以盲目地close()任何你不喜欢的文件描述符。如果您的某个库出于某种特定原因保留了开放式 FD 怎么办?
    • @user2136293:你到底想做什么? What is your real problem?
    猜你喜欢
    • 2012-04-29
    • 2021-05-23
    • 1970-01-01
    • 1970-01-01
    • 2021-03-03
    • 1970-01-01
    • 2011-10-08
    • 1970-01-01
    • 2011-01-31
    相关资源
    最近更新 更多