lea*_*ner 7 unix fork file-descriptor parent-child setrlimit
有没有办法遍历已经打开的文件描述符(由父进程打开)并在子进程中一一关闭它们?
操作系统:Unix。
关闭原因:setrlimit() 的 RLIMIT_NOFILE 限制限制了一个进程可以分配的文件描述符的数量。如果我们想通过设置这个限制来限制我们的子进程,它取决于已经分配的文件描述符。
尝试在子进程中设置此限制是受到限制的,因为父进程有一些打开的文件描述符,因此我们不能将此限制设置为小于该数字。
示例:如果父进程分配了 10 个文件描述符,并且我们希望将子进程文件描述符数量限制为小于 10(比如 3),我们将需要关闭子进程内的 7 个文件描述符。
对此的解决方案可以使所有想要限制其子进程创建新文件或打开新网络连接的人受益。
下面的习惯用法并不少见(取自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);
}
}
Run Code Online (Sandbox Code Playgroud)
(这是从 mimedefang-2.78 开始的,在以后的版本中实现略有改变。)
这有点像黑客攻击(正如 MIMEDefang 代码自由承认的那样)。在许多情况下,从 FD 3(或 )开始STDERR_FILENO+1而不是 0更有用。close()返回EBADF无效 FD,但这通常不会出现问题(至少在 C 中不会,在其他语言中可能会引发异常)。
由于您可以确定文件描述符上限,其getrlimit(RLIMIT_NOFILE,...)定义为:
RLIMIT_NOFILE
该数字比系统可以分配给新创建的描述符的最大值大一。如果超过此限制,分配文件描述符的函数将失败,并将 errno 设置为 [EMFILE]。此限制限制了进程可以分配的文件描述符的数量。
您可以使用此值(减去 1)作为循环的上限。上述 和ulimit -n、getconf OPEN_MAX和sysconf(OPEN_MAX)都应该一致。
由于open()始终分配最低的空闲 FD,因此最大打开文件数和最高 FD+1 是相同的数字。
要确定哪些 fd 已打开,请不要close()使用 no-op,如果 fd 未打开,该操作lseek(fd, 0, SEEK_CUR)将返回(尽管调用条件EBADF没有明显的好处)。的循环 0 ..调用/ 。lseek()close()socatfilanFD_SETSIZEfstat()fstat64()
守护任意进程的libslackdaemon实用程序也使用这种强力方法(同时确保在使用时保持前三个描述符打开inetd)。
如果您的程序可以跟踪文件句柄,最好这样做,或者在FD_CLOEXEC可用的情况下使用。然而,如果您希望进行防御性编码,您可能更愿意不信任您的父进程,例如由浏览器启动的外部处理程序/查看器进程,例如Unix 平台上这个长期存在且古老的 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 "$@"
Run Code Online (Sandbox Code Playgroud)
15 年后更新,当进程创建从 Netscape Portable Runtime (NSPR) API 更改为使用 LaunchApp 时,此问题在 Firefox 58 (2018) 中得到解决。