防止在Linux fork期间继承文件描述符

| 如何防止跨fork()系统调用复制文件描述符(当然,不关闭文件描述符)? 我正在寻找一种方法来将单个文件描述符标记为不是 (copy-)由孩子在fork()继承,类似于FD_CLOEXEC一样的hack,但对于fork(如果愿意,可以使用FD_DONTINHERIT功能)。 有人这样做吗?或对此进行了调查,对我有一个提示? 谢谢 更新: 我可以使用libc的__register_atfork
 __register_atfork(NULL, NULL, fdcleaner, NULL)
在fork()返回之前关闭child中的fds。但是,fds仍在被复制,因此对我来说这听起来像是一个愚蠢的破解。问题是如何跳过不需要的fds的子项中的dup()-ing 我在考虑一些需要fcntl(fd,F_SETFL,F_DONTINHERIT)的情况: fork()将复制事件fd(例如epoll);有时这是不需要的,例如FreeBSD将kqueue()事件fd标记为KQUEUE_TYPE,并且这些fds类型不会跨派复制(如果有想要从孩子那里使用它,它必须与共享的fd表一起使用) fork()会复制100k不需要的fds来分叉一个孩子来执行一些CPU密集型任务(假设对fork()的需求很低,并且程序员不希望为通常不会做的事情维护一个孩子的池) \不会发生) 有些描述符我们要复制(0,1,2),有些(其中大多数?)不是。我认为完整的fdtable duping在这里是出于历史原因,但我可能是错的。 这听起来有多愚蠢: 修补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;
}
当然,不能认为这是一个真正的长凳,但是无论如何:
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在2.20GHz,4GB内存的Dell Inspiron 1520Intel®Core 2 Duo CPU T7500上运行;平均负载= 0.00
已邀请:
不。您自己关闭它们,因为您知道哪些需要关闭。
如果您以调用
exec
函数的方式使用ѭ3can,则可以在once4ѭ之后将
fcntl
FD_CLOEXEC
结合使用来关闭文件描述符:
int fd = open(...);
fcntl(fd, F_SETFD, FD_CLOEXEC);
这样的文件描述符将在
fork
中幸存,但在
exec
系列中不起作用。
据我所知,没有标准的方法可以做到这一点。 如果您希望适当地实现它,则最好的方法可能是添加一个系统调用以将文件描述符标记为“近叉”,并拦截“ 11”个系统调用(系统调用编号2)以在调用原始的“ 11”之后对这些标志进行操作。 如果您不想添加新的系统调用,则可以通过截获
sys_ioctl
(系统调用号54)并添加一个新命令来标记文件描述为“分叉”来摆脱困境。 当然,如果您可以控制应用程序在做什么,那么最好维护要在fork上关闭的所有文件描述符的用户级表,然后调用自己的
myfork
。这将进行分叉,然后遍历用户级表,以关闭那些如此标记的文件描述符。 然后,您不必在Linux内核中摆弄,只有当您无法控制fork过程时(例如,如果第三方库正在执行
fork()
调用),该解决方案才可能是必需的。

要回复问题请先登录注册