作用
为新建的文件描述符使能 close-on-exec(执行exec时关闭) 标志,避免文件描述符无意间泄漏给了fork创建的子进。
当没有这个标志,打开文件时,得到的fd,将会被子进程继承,并且子进程会获得这个fd的读写能力。
往往父进程打开的文件,不希望子进程读写,所以,子进程启动之后,可以手动关闭fd。
但是关闭fd的操作不是原子,因此可以在父进程调用open打开文件的时候,设置标志O_CLOEXEC,从而使得子进程启动的时候,就把父进程继承而来的fd自动关闭。
示例代码
int fd = open("xxx", O_RDWR | O_CLOEXEC);
man帮助文档中的说明
# man open
O_CLOEXEC (since Linux 2.6.23)
Enable the close-on-exec flag for the new file descriptor. Specifying this flag permits a program to avoid additional fcntl(2) F_SETFD operations to set the FD_CLOEXEC flag.
Note that the use of this flag is essential in some multithreaded programs, because using a separate fcntl(2) F_SETFD operation to set the FD_CLOEXEC flag does not suffice to avoid race conditions where one thread opens a file descriptor and attempts to set its close-on-exec flag using fcntl(2) at the same time as another thread does a fork(2) plus execve(2).
Depending on the order of execution, the race may lead to the file descriptor returned by open() being unintentionally leaked to the program executed by the child process created by fork(2).
(This kind of race is in principle possible for any system call that creates a file descriptor whose close-on-exec flag should be set, and various other Linux system calls provide an equivalent of the O_CLOEXEC flag to deal with this problem.)
该标志的必要性
fork子进程以写时复制(COW,Copy-On-Write)方式获得父进程的数据空间、堆和栈副本,这其中也包括文件描述符
。刚刚fork成功时,父子进程中相同的
文件描述符
指向系统文件表中的同一项(这也意味着他们共享同一文件偏移量)。
接着,
一般子进程会调用exec执行另一个程序
,此时会用全新的程序替换子进程的正文,数据,堆和栈等。此时保存文件描述符的变量当然也不存在了,
导致无法关闭无用的文件描述符
了。所以会
fork子进程后在子进程中直接执行close关掉无用的文件描述符,然后再执行exec
。
但是在复杂系统中,有时我们fork子进程时已经不知道打开了多少个文件描述符(包括socket句柄等),这此时进行逐一清理确实有很大难度。我们期望的是能
在fork子进程前打开某个文件句柄时就指定好:“这个句柄我在fork子进程后执行exec时就关闭”。其实时有这样的方法的:即所谓的 close-on-exec。
close-on-exec的实现只需要调用系统的fcntl就能实现,很简单几句代码就能实现:
int fd=open("foo.txt",O_RDONLY);
int flags = fcntl(fd, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
这样,
当fork子进程后,仍然可以使用fd。但执行exec后系统就会字段关闭子进程中的fd了。