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

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

ОС: Unix.

Причина закрытия: ограничение RLIMIT_NOFILE для setrlimit () ограничивает количество файловых дескрипторов, которые может выделить процесс. Если мы хотим ограничить наш дочерний процесс, установив это ограничение, это зависит от уже выделенных файловых дескрипторов.

Попытка установить это ограничение в дочернем процессе ограничена, поскольку у родительского процесса есть несколько открытых файловых дескрипторов, и, следовательно, мы не можем установить это ограничение меньше этого числа.

Пример: если родительскому процессу выделено 10 файловых дескрипторов и мы хотим ограничить количество файловых дескрипторов дочернего процесса до менее 10 (скажем, 3), нам нужно будет закрыть 7 файловых дескрипторов внутри дочернего процесса.

Решение этой проблемы может принести пользу всем тем, кто хочет ограничить свой дочерний процесс от создания новых файлов или открытия новых сетевых подключений.


person learner    schedule 07.03.2013    source источник
comment
Если у родительского процесса выделено 10 файловых дескрипторов ...: что для вас означает выделение? Потому что файловые дескрипторы либо открываются, либо закрываются. Слово «выделение» связано с управлением памятью ...   -  person thkala    schedule 07.03.2013
comment
Исправление: я хотел сказать 10 открытых файловых дескрипторов родительского процесса, спасибо за исправление thkala   -  person learner    schedule 07.03.2013


Ответы (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 - это одно и то же число.

Чтобы определить, какие fd открыты, вместо close() используйте no-op lseek(fd, 0, SEEK_CUR), который вернет EBADF, если fd не открыт (хотя нет очевидной выгоды от вызова lseek() для условного close()). filan socat проходит через 0 .. FD_SETSIZE вызывает _18 _ / _ 19_.

libslack daemon утилита, которая демонизирует произвольные процессы, также использует этот метод грубой силы (при этом убедитесь, что три дескриптора открываются при использовании под inetd).

В случае, если ваша программа может отслеживать дескрипторы файлов, предпочтительнее это сделать или использовать FD_CLOEXEC, где это возможно. Однако, если вы хотите использовать защитный код, вы можете предпочесть не доверять своему родительскому процессу, например, для процесса внешнего обработчика / средства просмотра, запущенного браузером, например как эта давняя и древняя ошибка 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 "$@"
person mr.spuratic    schedule 01.04.2014

Насколько мне известно, в Unix / POSIX нет общего способа перебора дескрипторов открытых файлов. Традиционный способ решения проблемы, которую вы описываете, - отслеживать их в своем собственном коде, при необходимости используя структуру данных, такую ​​как массив или список, и закрывать их в дочернем процессе после fork(), но до exec().

Однако некоторые операционные системы предлагают потенциальное решение, если вы вызываете exec() после создания дочернего процесса. Либо путем установки флага FD_CLOEXEC для файлового дескриптора с использованием fcntl(), либо с помощью флага O_CLOEXEC для open() операционная система получает указание закрыть этот конкретный файловый дескриптор перед вызовом exec(). Вам нужно будет проконсультироваться с документацией вашей целевой операционной системы, чтобы узнать, поддерживаются ли и какие из этих флагов.

person thkala    schedule 07.03.2013
comment
Могу ли я перебрать номера файловых дескрипторов от 0 до МАКСИМАЛЬНО РАЗРЕШЕННЫХ файловых дескрипторов и принять вызов на основе вывода функции dup (int fildes)? Так как dup () в случае неудачи возвращает «-1». Это плохой способ или еще один грубый способ узнать информацию, которую я ищу? - person learner; 12.03.2013
comment
@ user2136293: в вашем предложении есть множество предположений, которые могут и сломают ваш код: 1. Вы предполагаете, что можете надежно узнать, каково максимальное число FD. Кроме MAX_INT нет другого надежного ограничения. 2. Вы предполагаете, что это число будет разумным. Собираетесь зондировать 2 ^ 31 ФД? 3. Вы предполагаете, что можете вслепую close() любой дескриптор файла, который вам не нравится. Что, если одна из ваших библиотек по какой-то конкретной причине сохраняет открытый FD? - person thkala; 13.03.2013
comment
@ user2136293: Что вы все равно пытаетесь сделать? В чем ваша настоящая проблема? - person thkala; 13.03.2013