close_on_exec选项:FD_CLOEXEC(fcntl)、O_CLOEXEC(open) 和 EPOLL_CLOEXEC(epoll_createl)

close_on_exec解决的问题类型:

我们经常会碰到一个进程需要fork出子进程的情况,而且子进程很可能会继续exec新的程序。这就不得不提到如何妥善处理好子进程中无用文件描述符的问题。


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了。


open()函数的flags参数可以传入O_CLOEXEC标记,man手册对其的描述如下:

[

l

i

n

u

x

2.6.23

]

_{[注意:linux 2.6.23才开始支持此标记]}

[linux2.6.23]
在这里插入图片描述
相近的还有epoll_fctl()函数当中的flags:EPOLL_CLOEXEC,使用fcntl()函数对文件描述符的close_on_exec属性进行设置的flags:FD_CLOEXEC(见上文示例代码);

这些宏的设定都是为了一步实现上面的提到的close-on-exec的效果。同时只要是和创建文件描述符或是对文件描述符的属性进行修改的函数的flags标志位一般都会保留实现close-on-exec的相关宏,只是前缀名不同,但是都以CLOEXEC结尾。

THE END
< <上一篇
下一篇>>