Как вы предотвращаете копирование наследования файлов через 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 здесь по историческим причинам, но я, вероятно, ошибаюсь.
Как глупо это звучит:
- patch fcntl для поддержки флага dontinherit в дескрипторах файлов (не уверен, что флаг должен храниться в 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;
}
конечно, не может считать это настоящей скамейкой, но так или иначе:
[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