在 Linux fork 期间防止文件描述符继承
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5713242/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Prevent file descriptors inheritance during Linux fork
提问by user237419
How do you prevent a file descriptor from being copy-inherited across fork() syscalls (without closing it, of course) ?
你如何防止文件描述符在 fork() 系统调用之间被复制继承(当然不关闭它)?
I am looking for a way to mark a single file descriptoras NOT to be (copy-)inheritedby children at fork(), something like a FD_CLOEXEC-like hack but for forks (so a FD_DONTINHERIT feature if you like). Anybody did this? Or looked into this and has a hint for me to start with?
我正在寻找一种方法来将单个文件描述符标记 为不被fork() 的孩子(复制)继承,类似于 FD_CLOEXEC 的 hack 但对于 fork(所以如果你愿意,可以使用 FD_DONTINHERIT 功能)。有人这样做过吗?或者研究这个并有一个提示让我开始?
Thank you
谢谢
UPDATE:
更新:
I could use libc's __register_atfork
__register_atfork(NULL, NULL, fdcleaner, NULL)
to close the fds in child just before fork() returns. However, the fds are still being copied so this sounds like a silly hack to me. Question is how to skip the dup()-ing in child of unneeded fds
在 fork() 返回之前关闭 child 中的 fds。然而,fds 仍在被复制,所以这对我来说听起来像是一个愚蠢的黑客。问题是如何在不需要的 fds 的孩子中跳过 dup()-ing
I'm thinking of some scenarios when a fcntl(fd,F_SETFL,F_DONTINHERIT) would be needed:
我正在考虑需要 fcntl(fd,F_SETFL,F_DONTINHERIT) 的一些场景:
fork() will copy an event fd (e.g. epoll); sometimes this isn't wanted, for example FreeBSD is marking the kqueue() event fd as being of a KQUEUE_TYPE and these types of fds won't be copied across forks (the kqueue fds are skipped explicitly from being copied, if one wants to use it from a child it must fork with shared fd table)
fork() will copy 100k unneeded fds to fork a child for doing some cpu-intensive tasks (suppose the need for a fork() is probabilistically very low and programmer won't want to maintain a pool of children for something that normally wouldn't happen)
fork() 将复制一个事件 fd(例如 epoll);有时这不是想要的,例如 FreeBSD 将 kqueue() 事件 fd 标记为 KQUEUE_TYPE 并且这些类型的 fds 不会跨叉复制(如果想要复制的话,kqueue fds 会被显式跳过从孩子那里使用它,它必须与共享 fd 表分叉)
fork() 将复制 100k 不需要的 fds 来 fork 一个孩子来执行一些 CPU 密集型任务(假设对 fork() 的需求概率非常低,并且程序员不想为通常不会的事情维护一个孩子池不会发生)
Some descriptors we want to be copied (0,1,2), some (most of them?) not. I think full fdtable duping is here for historic reasons but I am probably wrong.
我们想要复制一些描述符(0,1,2),一些(大多数?)不。我认为完整的 fdtable 复制是出于历史原因,但我可能错了。
How silly does this sound:
这听起来多么愚蠢:
- patch fcntl to support the dontinheritflag on file descriptors (not sure if the flag should be kept per-fd or in a fdtable fd_set, like the close-on-exec flags are being kept
- modify dup_fd() in kernel to skip copying of dontinheritfds, same as freebsd does for kq fds
- 修补 fcntl 以支持文件描述符上的dontinherit标志(不确定该标志是应保留在每个 fd 中还是应保留在 fdtable fd_set 中,就像保留了 close-on-exec 标志一样
- 修改内核中的 dup_fd() 以跳过dontinheritfds 的复制,与 freebsd对kqfds 所做的相同
consider the program
考虑程序
#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;
}
ofcourse, can't consider this a real bench but anyhow:
当然,不能认为这是一个真正的板凳,但无论如何:
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 ran on a Dell Inspiron 1520 Intel(R) Core(TM)2 Duo CPU T7500 @ 2.20GHz with 4GB ram; average_load=0.00
forkit 在 Dell Inspiron 1520 Intel(R) Core(TM)2 Duo CPU T7500 @ 2.20GHz 和 4GB 内存上运行;平均负载=0.00
采纳答案by Ignacio Vazquez-Abrams
No. Close them yourself, since you know which ones need to be closed.
不。你自己关闭它们,因为你知道哪些需要关闭。
回答by paxdiablo
There's no standard way of doing this to my knowledge.
据我所知,没有标准的方法可以做到这一点。
If you're looking to implement it properly, probably the best way to do it would be to add a system call to mark the file descriptor as close-on-fork, and to intercept the sys_fork
system call (syscall number 2) to act on those flags after calling the original sys_fork
.
如果您希望正确实现它,最好的方法可能是添加一个系统调用以将文件描述符标记为 close-on-fork,并拦截sys_fork
系统调用(系统调用编号 2)以对其进行操作调用原始sys_fork
.
If you don't want to add a new system call, you might be able to get away with intercepting sys_ioctl
(syscall number 54) and just adding a new command to it for marking a file description close-on-fork.
如果您不想添加新的系统调用,您可能sys_ioctl
可以避免拦截(系统调用编号 54),只需向其中添加一个新命令来标记文件描述 close-on-fork。
Of course, if you can control what your application is doing, then it might be better to maintain user-level tables of all file descriptors you want closed on fork and call your own myfork
instead. This would fork, then go through the user-level table closing those file descriptors so marked.
当然,如果您可以控制您的应用程序正在做什么,那么最好维护您要在 fork 上关闭的所有文件描述符的用户级表,并myfork
改为调用您自己的表。这将分叉,然后通过用户级表关闭这些标记的文件描述符。
You wouldn't have to fiddle around in the Linux kernel then, a solution that's probably only necessary if you don't have control over the fork process (say, if a third party library is doing the fork()
calls).
那么你就不必在 Linux 内核中摆弄了,这个解决方案可能只有在你无法控制 fork 进程的情况下才是必要的(比如,如果第三方库正在执行fork()
调用)。
回答by zneak
If you fork
with the purpose of calling an exec
function, you can use fcntl
with FD_CLOEXEC
to have the file descriptor closed once you exec
:
如果您fork
的目的是调用exec
函数,则可以使用fcntl
withFD_CLOEXEC
关闭文件描述符exec
:
int fd = open(...);
fcntl(fd, F_SETFD, FD_CLOEXEC);
Such a file descriptor will survive a fork
but not functions of the exec
family.
这样的文件描述符将fork
在exec
家族的函数中存活,但不能存活。