Ответ 1
Нет. Закройте их самостоятельно, так как вы знаете, какие из них нужно закрыть.
Как вы предотвращаете копирование наследования файлов через fork() syscalls (без его закрытия, конечно)?
Я ищу способ отметить один дескриптор файла как НЕ, чтобы быть (copy-) унаследовал детей в fork(), что-то вроде взлома FD_CLOEXEC, но для forks (так что функция FD_DONTINHERIT, если хотите). Кто-нибудь это сделал? Или посмотрел на это и дал мне подсказку для начала?
Спасибо
ОБНОВЛЕНИЕ:
Я мог бы использовать libc __register_atfork
__register_atfork(NULL, NULL, fdcleaner, NULL)
чтобы закрыть fds в дочернем элементе только перед тем, как fork() вернется. Тем не менее, fds все еще копируются, так что это звучит как глупый хак для меня. Вопрос в том, как пропустить dup() в дочернем объекте ненужных fds
Я думаю о некоторых сценариях, когда нужны fcntl (fd, F_SETFL, F_DONTINHERIT):
fork() скопирует событие fd (например, epoll); иногда это не требуется, например, FreeBSD отмечает событие fk kqueue() как имеющее значение KQUEUE_TYPE, и эти типы fds не будут скопированы через forks (kqueue fds пропускается явно от копирования, если вы хотите использовать его у ребенка, который он должен использовать fork с общей таблицей fd)
fork() скопирует 100k ненужных fds, чтобы развить ребенка для выполнения некоторых интенсивных задач (предположим, что необходимость fork() вероятностно очень низкая, и программист не захочет поддерживать пул детей для чего-то, чего обычно не будет)
Некоторые дескрипторы, которые мы хотим скопировать (0,1,2), некоторые (большинство из них?) нет. Я думаю, что полное обманывание fdtable здесь по историческим причинам, но я, вероятно, ошибаюсь.
Как глупо это звучит:
рассмотрим программу
#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;
}
конечно, не может считать это настоящей скамейкой, но так или иначе:
[email protected]:/home/cia/dev/kqueue# time ./forkit
fork_cost(100 fds)=0.000000ticks(0.000000 seconds)
real 0m0.004s
user 0m0.000s
sys 0m0.000s
[email protected]:/home/cia/dev/kqueue# gcc -DNUMFDS=100000 -o forkit forkit.c
[email protected]:/home/cia/dev/kqueue# time ./forkit
fork_cost(100000 fds)=10000.000000ticks(0.010000 seconds)
real 0m0.287s
user 0m0.010s
sys 0m0.240s
[email protected]:/home/cia/dev/kqueue# gcc -DNUMFDS=100 -o forkit forkit.c
[email protected]:/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 (R) Core (TM) 2 Duo CPU T7500 @2.20GHz с 4 ГБ оперативной памяти; average_load = 0.00
Нет. Закройте их самостоятельно, так как вы знаете, какие из них нужно закрыть.
Если вы fork
с целью вызова функции exec
, вы можете использовать fcntl
с FD_CLOEXEC
, чтобы дескриптор файла был закрыт после exec
:
int fd = open(...);
fcntl(fd, F_SETFD, FD_CLOEXEC);
Такой файловый дескриптор выдержит fork
, но не функции семейства exec
.
Нет стандартного способа сделать это, насколько мне известно.
Если вы хотите правильно его реализовать, вероятно, лучший способ сделать это - добавить системный вызов, чтобы пометить дескриптор файла как закрытую-fork, и перехватить системный вызов sys_fork
(syscall номер 2), чтобы действовать на эти флаги после вызова оригинала sys_fork
.
Если вы не хотите добавлять новый системный вызов, вы можете уйти с перехватом sys_ioctl
(syscall номер 54) и просто добавить к нему новую команду для маркировки описания файла close- вилка.
Конечно, если вы можете контролировать то, что делает ваше приложение, лучше было бы поддерживать таблицы пользовательского уровня всех дескрипторов файлов, которые вы хотите закрыть на fork, и вместо этого вызвать свой собственный myfork
. Это будет fork, а затем просмотрите таблицу пользовательского уровня, закрыв эти дескрипторы файлов таким образом.
В Linux-ядре вам не пришлось бы работать, это решение, которое, вероятно, необходимо только в том случае, если у вас нет контроля над процессом fork (скажем, если сторонняя библиотека выполняет вызовы fork()
),.