关于linux进程间的close-on-exec机制


  1. 通过fcntl设置FD_CLOEXEC标志有什么用?  
  2.  close on exec, not on-fork, 意为如果对描述符设置了FD_CLOEXEC,使用execl执行的程序里,此描述符被关闭,不能再使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可使用。 

         子进程在fork出来的时候,使用了写时复制(COW,Copy-On-Write)方式获得父进程的数据空间、 堆和栈副本,这其中也包括文件描述符。刚刚fork成功时,父子进程中相同的文件描述符指向系统文件表中的同一项(这也意味着他们共享同一文件偏移量)。这其中当然也包含父进程创建的socket。

接着,一般我们会调用exec执行另一个程序,此时会用全新的程序替换子进程的正文,数据,堆和栈等。此时保存文件描述符的变量当然也不存在了,我们就无法关闭无用的文件描述符了。所以通常我们会fork子进程后在子进程中直接执行close关掉无用的文件描述符,然后再执行exec。

但是在复杂系统中,有时我们fork子进程时已经不知道打开了多少个文件描述符(包括socket句柄等),这此时进行逐一清理确实有很大难度。我们期望的是能在fork子进程前打开某个文件句柄时就指定好:“这个句柄我在fork子进程后执行exec时就关闭”。其实时有这样的方法的:即所谓 的 close-on-exec。

回到我们的应用场景中来,只要我们在创建socket的时候加上SOCK_CLOEXEC标志,就能够达到我们要求的效果,在fork子进程中执行exec的时候,会清理掉父进程创建的socket。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #ifdef WIN32  
  2.     SOCKET ss = ::socket(PF_INET, SOCK_STREAM, 0);  
  3. #else  
  4.     SOCKET ss = ::socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);  
  5. #endif  
当然,其他的文件描述符也有类似的功能,例如文件,可以在打开的时候使用O_CLOEXEC标识(linux 2.6.23才开始支持此标记),达到和上面一样的效果。或者使用系统的fcntl函数设置FD_CLOEXEC即可。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //方案A  
  2. int fd = open(“foo.txt”,O_RDONLY);  
  3. int flags = fcntl(fd, F_GETFD);  
  4. flags |= FD_CLOEXEC;  
  5. fcntl(fd, F_SETFD, flags);  
  6. //方案B,linux 2.6.23后支持  
  7. int fd = open(“foo.txt”,O_RDONLY | O_CLOEXEC);  
好了,现在我们终于可以完美的解决端口占用这个令人烦恼的问题了。
总结:父进程对文件描述符指定O_CLOEXEC标记,并且父进程fork了子进程,而子进程执行exec后,子进程从父进程继承过来的该文件描述符就会关闭。或者说任何一个进程对一个文件描述符指定了O_CLOEXEC标记,那该进程执行exec后,该句柄就会关闭。

发布了42 篇原创文章 · 获赞 27 · 访问量 18万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览