当前位置: 首页 > 面试题库 >

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

谈萧迟
2023-03-14
问题内容

如何防止跨fork()系统调用复制文件描述符(当然,不关闭文件描述符)?

我正在寻找一种方式来纪念 一个文件描述符 作为 不被继承(复印通)
(如果你喜欢这么FD_DONTINHERIT功能)通过孩子fork()的,有点像FD_CLOEXEC般的技巧,但对于叉。有人这样做吗?或对此进行了调查,对我有一个提示?

谢谢

更新:

我可以使用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类型不会跨派复制(如果要复制kqueue 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


问题答案:

不。您自己关闭它们,因为您知道哪些需要关闭。



 类似资料:
  • 文件描述符 Linux很重要的设计思想就是一切皆文件,网络是文件,键盘等外设也是文件,很神奇吧?于是所有资源都有了统一的接口,开发者可以像写文件那样通过网络传输数据,我们也可以通过/proc/的文件看到进程的资源使用情况。 内核给每个访问的文件分配了文件描述符(File Descriptor),它本质是一个非负整数,在打开或新建文件时返回,以后读写文件都要通过这个文件描述符了。 应用 我们想想操作

  • 文件描述符接口 函数 int  fd_new (void)   分配文件描述符   struct dfs_fd *  fd_get (int fd)   获取文件描述结构   void  fd_put (struct dfs_fd *fd)   放置文件描述符   int  fd_is_open (const char *pathname)   判断文件是否已被打开   int  select (

  • 问题内容: 有没有办法在原始文件描述符而不是FILE *上执行ftell()的操作(返回文件中的当前位置)?我认为应该有,因为您可以使用lseek()查找原始文件描述符。 我知道我可以使用fdopen()创建与文件描述符相对应的FILE *,但我宁愿不这样做。 问题答案: 只需使用:

  • 当使用了大量虚拟主机,而且每个主机又使用了不同的日志文件时,Apache可能会遭遇文件描述符(有时也称为文件句柄)耗尽的困境。Apache使用的文件描述符总数如下:每个不同的错误日志文件一个、每个其他日志文件指令一个、再加10-20个作为内部使用。Unix操作系统限制了每个进程可以使用的文件描述符数量。典型上限是64个,但可以进行扩充,直至到达一个很大的硬件限制为止(hard-limit)。 尽管

  • 处理文件描述符 尽管很不像,但是在大多操作系统中,标准输入输出流 stdin 和 stdout 虽然叫做「流」,但它们都有文件的接口。我们同样也会将它们实现成为文件。 但是不用担心,作为文件的许多功能,stdin 和 stdout 都不会支持。我们只需要为其实现最简单的读写接口。 进程打开的文件 操作系统需要为进程维护一个进程打开的文件清单。其中,一定存在的是 stdin stdout 和 std

  • 问题 你有多个Python解释器进程在同时运行,你想将某个打开的文件描述符从一个解释器传递给另外一个。 比如,假设有个服务器进程相应连接请求,但是实际的相应逻辑是在另一个解释器中执行的。 解决方案 为了在多个进程中传递文件描述符,你首先需要将它们连接到一起。在Unix机器上,你可能需要使用Unix域套接字, 而在windows上面你需要使用命名管道。不过你无需真的需要去操作这些底层, 通常使用 m