如何防止文件描述符在fork()系统调用中被复制继承(当然不关闭它)?
我正在寻找一种方法将单个文件描述符标记为不是 (copy-)在fork()中由子项继承,类似于FD_CLOEXEC之类的hack但是for forks(如果你愿意的话,还有FD_DONTINHERIT功能)。 有人这样做过吗?或者看看这个,并提示我开始?
谢谢
更新:
__register_atfork(NULL, NULL, fdcleaner, NULL)
在fork()返回之前关闭子节点中的fds。然而,fds仍然被复制,所以这对我来说听起来像是一个愚蠢的黑客。问题是如何跳过不需要的fds的孩子中的dup() -
我正在考虑一些需要fcntl(fd,F_SETFL,F_DONTINHERIT)的场景:
fork()将复制事件fd(例如epoll);有时这不是必需的,例如FreeBSD将kqueue()事件fd标记为KQUEUE_TYPE,并且这些类型的fds不会被复制到forks(如果有人想要,则会明确跳过kqueue fds被复制)从一个孩子那里使用它,它必须用共享的fd表分叉)
fork()会复制100k不需要的fds来分叉一个孩子做一些cpu密集型任务(假设需要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;
}
当然,无论如何都不能认为这是一个真正的替补:
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(R)Core(TM)2 Duo CPU T7500 @ 2.20GHz上运行,配备4GB内存; average_load = 0.00
答案 0 :(得分:8)
如果您fork
的目的是调用exec
函数,则可以fcntl
与FD_CLOEXEC
一起使exec
使文件描述符关闭int fd = open(...);
fcntl(fd, F_SETFD, FD_CLOEXEC);
:
fork
这样的文件描述符将在exec
但不是{{1}}系列的函数中存活。
答案 1 :(得分:7)
没有。自己关闭它们,因为你知道哪些需要关闭。
答案 2 :(得分:3)
据我所知,没有标准的方法可以做到这一点。
如果您希望正确实现它,可能最好的方法是添加系统调用以将文件描述符标记为close-on-fork,并截取sys_fork
系统调用(系统调用号2)在调用原始sys_fork
后对这些标志执行操作。
如果您不想添加新的系统调用,您可以放弃拦截sys_ioctl
(系统调用号54),只需添加一个新命令即可将文件描述标记为关闭上叉。
当然,如果您可以控制应用程序正在执行的操作,那么最好维护您希望在fork上关闭的所有文件描述符的用户级表,而是调用您自己的myfork
。这将分叉,然后通过用户级表关闭那些标记的文件描述符。
你不必在Linux内核中乱搞,这个解决方案可能只有在你无法控制fork进程时才需要(比方说,如果第三方库正在执行fork()
呼叫)。