Закрытие открытых файловых дескрипторов в дочернем процессе
Есть ли способ перебирать уже открытые файловые дескрипторы (открытые родительским процессом) и закрывать их один за другим в дочернем процессе?
ОС: Unix.
Причина закрытия: ограничение RLIMIT_NOFILE в setrlimit() ограничивает количество файловых дескрипторов, которые может выделить процесс. Если мы хотим ограничить наш дочерний процесс, установив этот предел, это зависит от уже выделенных файловых дескрипторов.
Попытка установить этот предел в дочернем процессе ограничена, так как у родительского процесса есть несколько открытых файловых дескрипторов, и, следовательно, мы не можем установить этот предел меньше этого числа.
Пример: если родительскому процессу выделено 10 файловых дескрипторов, и мы хотим ограничить номер дескриптора файла дочернего процесса менее 10 (скажем, 3), нам нужно закрыть 7 файловых дескрипторов внутри дочернего процесса.
Решение этой проблемы может принести пользу всем тем, кто хочет ограничить свой дочерний процесс от создания новых файлов или открытия новых сетевых подключений.
2 ответа
Следующая идиома не является редкостью (это взято из части C MIMEDefang):
/* 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()
возвращается EBADF
с недопустимым FD, но обычно это не создает проблем (по крайней мере, в C, в других языках может быть выдано исключение).
Поскольку вы можете определить верхний предел дескриптора файла с помощью getrlimit(RLIMIT_NOFILE,...)
который определяется как:
RLIMIT_NOFILE
Это на единицу больше максимального значения, которое система может присвоить вновь созданному дескриптору. Если этот предел превышен, функции, которые выделяют файловый дескриптор, должны завершиться с ошибкой, когда errno установлено в [EMFILE]. Этот предел ограничивает количество файловых дескрипторов, которые может выделить процесс.
Вы можете использовать это (вычитая 1) в качестве верхнего предела цикла. Выше и ulimit -n
, getconf OPEN_MAX
а также sysconf(OPEN_MAX)
должны все согласиться.
поскольку open()
всегда назначает самый низкий свободный FD, максимальное количество открытых файлов и самое большое FD+1 - это одно и то же число.
Либслэк daemon
Утилита, которая демонизирует произвольные процессы, также использует этот подход (при этом убедитесь, что первые три дескриптора остаются открытыми при использовании в inetd
).
В случае, когда ваша программа может отслеживать файловые дескрипторы, предпочтительно сделать это или использовать FD_CLOEXEC
где доступно. Однако, если вы хотите защищать код, вы можете не доверять родительскому процессу, скажем, при запуске процесса внешнего обработчика / средства просмотра, запущенного браузером, например, такой как 12- летняя ошибка Mozilla в 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 "$@"
Насколько я знаю, в Unix/POSIX нет общего способа перебирать дескрипторы открытых файлов. Традиционный способ решения проблемы, которую вы описываете, состоит в том, чтобы отслеживать их в своем собственном коде, при необходимости, используя структуру данных, такую как массив или список, и закрывать их в дочернем процессе после fork()
но прежде exec()
,
Однако некоторые операционные системы предлагают потенциальное решение, если вы звоните exec()
после создания дочернего процесса. Либо, установив FD_CLOEXEC
флаг для файлового дескриптора, используя fcntl()
или с O_CLOEXEC
флаг для open()
операционная система получает указание закрыть этот конкретный дескриптор файла перед вызовом exec()
, Вам нужно будет обратиться к документации вашей целевой операционной системы, чтобы узнать, поддерживаются ли и какие из этих флагов.