Запретить наследование файловых дескрипторов во время форка Linux - PullRequest
23 голосов
/ 19 апреля 2011

Как вы предотвращаете наследование файлового дескриптора через системные вызовы fork () (конечно, не закрывая его)?

Я ищу способ пометить дескриптор одного файла как НЕ для наследования (copy-) дочерними объектами fork (), что-то вроде FD_CLOEXEC-подобного хака, но для вилок (так что, если хотите, функция FD_DONTINHERIT).Кто-нибудь делал это?Или посмотрел на это и у меня есть подсказка для начала?

Спасибо

ОБНОВЛЕНИЕ:

Я мог бы использовать libc's __register_atfork

 __register_atfork(NULL, NULL, fdcleaner, NULL)

, чтобы закрыть fds в child перед возвратом fork ().Тем не менее, fds все еще копируются, так что это звучит глупо для меня.Вопрос в том, как пропустить dup () - в дочернем элементе ненужных fds

. Я думаю о некоторых сценариях, когда потребуется fcntl (fd, F_SETFL, F_DONTINHERIT):

  • fork () скопирует событие fd (например, epoll);иногда это не нужно, например, FreeBSD помечает событие kqueue () fd как KQUEUE_TYPE, и эти типы fds не будут копироваться на вилки (fque kqueue пропускается явно из-за копирования, если кто-то хочетиспользуйте его от дочернего элемента, он должен быть разветвлен с общей таблицей fd)

  • fork () скопирует 100 000 ненужных fds для разветвления дочернего элемента для выполнения некоторых ресурсоемких задач (предположим, что требуетсяfork () очень маловероятно, и программисту не захочется поддерживать пул дочерних элементов для чего-то, что обычно не происходит)

Некоторые дескрипторы, которые мы хотим скопировать (0,1,2), некоторые (большинство из них?) Нет.Я думаю, что полное дублирование fdtable здесь по историческим причинам, но я, вероятно, ошибаюсь.

Как глупо звучит этот звук:

  • патч fcntl для поддержки флага dontinherit в файловых дескрипторах (не уверен, следует ли хранить флаг per-fd или в fdtable fd_set, например, флаги close-on-exec сохраняются
  • , изменяют dup_fd () в ядре, чтобы пропустить копирование dontinherit fds, так же, как и freebsd для kq fds

считают программу

#include <stdio.h>
#include <unistd.h>
#include <err.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>

static int fds[NUMFDS];
clock_t t1;

static void cleanup(int i)
{
    while(i-- >= 0) close(fds[i]);
}
void clk_start(void)
{
    t1 = clock();
}
void clk_end(void)
{  

    double tix = (double)clock() - t1;
    double sex = tix/CLOCKS_PER_SEC;
    printf("fork_cost(%d fds)=%fticks(%f seconds)\n",
        NUMFDS,tix,sex);
}
int main(int argc, char **argv)
{
    pid_t pid;
    int i;
    __register_atfork(clk_start,clk_end,NULL,NULL);
    for (i = 0; i < NUMFDS; i++) {
        fds[i] = open("/dev/null",O_RDONLY);
        if (fds[i] == -1) {
            cleanup(i);
            errx(EXIT_FAILURE,"open_fds:");
        }
    }
    t1 = clock();
    pid = fork();
    if (pid < 0) {
        errx(EXIT_FAILURE,"fork:");
    }
    if (pid == 0) {
        cleanup(NUMFDS);
        exit(0);
    } else {
        wait(&i);
        cleanup(NUMFDS);
    }
    exit(0);
    return 0;
}

курса, не могу считать это реальной скамейкой, но так или иначе:

root@pinkpony:/home/cia/dev/kqueue# time ./forkit
fork_cost(100 fds)=0.000000ticks(0.000000 seconds)

real    0m0.004s
user    0m0.000s
sys     0m0.000s
root@pinkpony:/home/cia/dev/kqueue# gcc -DNUMFDS=100000 -o forkit forkit.c
root@pinkpony:/home/cia/dev/kqueue# time ./forkit
fork_cost(100000 fds)=10000.000000ticks(0.010000 seconds)

real    0m0.287s
user    0m0.010s
sys     0m0.240s
root@pinkpony:/home/cia/dev/kqueue# gcc -DNUMFDS=100 -o forkit forkit.c
root@pinkpony:/home/cia/dev/kqueue# time ./forkit
fork_cost(100 fds)=0.000000ticks(0.000000 seconds)

real    0m0.004s
user    0m0.000s
sys     0m0.000s

forkit работал на процессоре Dell Inspiron 1520 Intel® Core ™ 2 Duo T7500 @ 2,20 ГГц с 4 ГБ ОЗУ; средняя загрузка = 0,00

Ответы [ 3 ]

8 голосов
/ 18 апреля 2013

Если вы fork с целью вызова функции exec, вы можете использовать fcntl с FD_CLOEXEC, чтобы закрыть дескриптор файла после того, как вы exec:

int fd = open(...);
fcntl(fd, F_SETFD, FD_CLOEXEC);

Такой файловый дескриптор выживет fork, но не функции семейства exec.

7 голосов
/ 19 апреля 2011

Нет. Закройте их сами, так как вы знаете, какие из них нужно закрыть.

3 голосов
/ 19 апреля 2011

Насколько мне известно, стандартного способа сделать это не существует.

Если вы хотите реализовать его правильно, вероятно, лучший способ сделать это - добавить системный вызов, чтобы пометить дескриптор файла какзакрытие на вилке и перехват системных вызовов sys_fork (системный вызов № 2) для воздействия на эти флаги после вызова исходного sys_fork.

Если вы не хотите добавлять новую системуcall, вы можете избежать перехвата sys_ioctl (системный вызов 54) и просто добавить к нему новую команду для пометки описания файла при закрытии.

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

Тогда вам не придется возиться с ядром Linux, решение, которое, вероятно, необходимо, только если вы этого не сделаетеконтролировать процесс ветвления (скажем, если сторонняя библиотека выполняет вызовы fork()).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...