以下习语并不罕见(这是从
MIMEDefang的C部分中摘录的):
#define CLOSEFDS 256
...
static void
closefiles(void)
{
int i;
for (i=0; i<CLOSEFDS; i++) {
(void) close(i);
}
}
(这是来自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是相同的数字。
为了确定打开了哪些文件描述符,可以使用无操作的
lseek(fd, 0, SEEK_CUR)
代替
close()
,如果fd未打开,则会返回
EBADF
(虽然对于有条件的
close()
调用来说,调用
lseek()
没有明显的好处)。
socat
的
filan
循环遍历0..
FD_SETSIZE
,调用
fstat()
/
fstat64()
。
libslack daemon
utility是将任意进程守护化的工具,也使用这种暴力方法(在
inetd
下使用时确保前三个描述符保持打开状态)。
如果您的程序可以跟踪文件句柄,最好这样做,或者在可用时使用
FD_CLOEXEC
。但是,如果您希望进行防御性编码,可能更喜欢不信任父进程,例如对于由浏览器启动的外部处理程序/查看器进程,例如Unix平台上的
长期存在的和古老的Mozilla bug。
对于偏执狂者(您是否希望您的PDF查看器继承包括缓存和打开TCP连接在内的每个打开的Firefox FD?):
#!/bin/bash
for ((fd=3; fd<=255; fd++)); do
exec {fd}<&-
done
exec /usr/local/bin/xpdf "$@"
自 15 年后,Firefox 58(2018 年)将进程创建从 Netscape Portable Runtime (NSPR) API 改为使用 LaunchApp 后,该问题得到解决。