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

标签 linux fork

如何防止文件描述符在 fork() 系统调用中被复制继承(当然是在不关闭它的情况下)?

我正在寻找一种方法将单个文件描述符标记为NOT to be (copy-)inherited by children at fork(),类似于 FD_CLOEXEC 类 hack,但用于 fork ​​(如果您愿意,可以使用 FD_DONTINHERIT 功能)。有人这样做过吗?或者调查了这个并为我提供了开始的提示?

谢谢

更新:

我可以使用 libc's __register_atfork

 __register_atfork(NULL, NULL, fdcleaner, NULL)

fork() 返回之前关闭 child 中的 fds。然而,FD 仍在被复制,所以这对我来说听起来像是一个愚蠢的 hack。问题是如何跳过不需要的 FD 的子级中的 dup()ing。

我正在考虑需要 fcntl(fd, F_SETFL, F_DONTINHERIT) 的一些场景:

  • fork() 将复制一个事件 FD(例如 epoll());有时这是不需要的,例如 FreeBSD 将 kqueue() 事件 FD 标记为 KQUEUE_TYPE 并且这些类型的 FD 不会跨叉复制(如果有人想要,kqueue FD 会被显式跳过复制)从 child 那里使用它,它必须使用共享的 FD 表进行 fork )

  • fork() 将复制 100k 不需要的 FD 以 fork child 执行一些 CPU 密集型任务(假设需要 fork() 的概率是非常低,程序员不想为通常不会发生的事情维护一个子池)

一些我们想要复制的描述符(0、1、2),一些(大部分?)不是。我认为完整的 FD 表复制是出于历史原因,但我可能错了。

这听起来有多傻:

  • 修补 fcntl() 以支持文件描述符上的 dontinherit 标志(不确定该标志是否应保留在每个 FD 或 FD 表 fd_set 中,例如close-on-exec 标志被保留
  • 修改内核中的 dup_fd() 以跳过 dontinherit FD 的复制,与 FreeBSD 对 kq FD 所做的相同

考虑程序

#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 RAM 上运行; average_load=0.00

最佳答案

如果你 fork 的目的是调用一个 exec 函数,你可以使用 fcntlFD_CLOEXEC 来在您exec 后关闭文件描述符:

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

这样的文件描述符将在 fork 中存活,但不会在 exec 家族的函数中存活。

关于linux - 在 Linux fork 期间防止文件描述符继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5713242/

相关文章:

c - 两个进程写入同一个文件

c - 信号处理程序读取不正确的值

android - 为android交叉编译c程序

c++ - 使用 sendfile() 通过线程或其他高效的复制文件方法复制文件

c - 附加到 C 中的进程

c - wait() 和 exit() 与父子进程

c - 如何在 20 秒后终止 c 程序

ruby-on-rails - 为 Rails 项目创建一个 gem

c - 生成文件: Pass a target's dependency as an argument

linux - 无背景和无窗口的 OpenGL 应用程序