关闭子进程中打开的文件描述符

时间:2013-03-07 08:39:54

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

有没有办法迭代已打开的文件描述符(由父进程打开)并在子进程中逐个关闭它们?

操作系统:Unix。

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

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

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

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

2 个答案:

答案 0 :(得分:3)

以下习语并不罕见(这取自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);
   }
}

这是一种黑客行为(正如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数相同。

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

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

对于偏执狂(你希望你的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 "$@"

答案 1 :(得分:0)

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

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