关于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后,该句柄就会关闭。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值