Закрытие открытых файловых дескрипторов в дочернем процессе

Есть ли способ перебирать уже открытые файловые дескрипторы (открытые родительским процессом) и закрывать их один за другим в дочернем процессе?

ОС: 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(), Вам нужно будет обратиться к документации вашей целевой операционной системы, чтобы узнать, поддерживаются ли и какие из этих флагов.

Другие вопросы по тегам