O_CLOEXEC


http://blog.csdn.net/chrisniu1984/article/details/7050663

 

我们经常会碰到需要fork子进程的情况,而且子进程很可能会继续exec新的程序。这就不得不提到子进程中无用文件描述符的问题!

 

fork函数的使用本不是这里讨论的话题,但必须提一下的是:子进程以写时复制(COW,Copy-On-Write)方式获得父进程的数据空间、堆和栈副本,这其中也包括文件描述符。刚刚fork成功时,父子进程中相同的文件描述符指向系统文件表中的同一项(这也意味着他们共享同一文件偏移量)。

 

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

 

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

 

 close-on-exec的实现只需要调用系统的fcntl就能实现,很简单几句代码就能实现:

[cpp]  view plain  copy
 
 print?
  1. int fd=open("foo.txt",O_RDONLY);  
  2. int flags = fcntl(fd, F_GETFD);  
  3. flags |= FD_CLOEXEC;  
  4. fcntl(fd, F_SETFD, flags);  

这样,当fork子进程后,仍然可以使用fd。但执行exec后系统就会字段关闭子进程中的fd了。

 

-------------------------------------------------------- 分割线 ------------------------------------------------------------------------------------

最近好好看了一下open函数,其中flags参数可以传入O_CLOEXEC标记 [注意:linux 2.6.23才开始支持此标记]

 

这样就可以一步实现上面的提到的close-on-exec的效果。
 
 
 
=============================
http://blog.csdn.net/ustc_dylan/article/details/6930189
 
通过fcntl设置FD_CLOEXEC标志有什么用?
close on exec, not on-fork, 意为如果对描述符设置了FD_CLOEXEC,使用execl执行的程序里,此描述符被关闭,不能再使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可使用。
eg:
jamie@jamie-laptop:~$ cat test.c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
        int fd,pid;
        char buffer[20];
        fd=open("wo.txt",O_RDONLY);
        printf("%d/n",fd);
        int val=fcntl(fd,F_GETFD);
        val|=FD_CLOEXEC;
        fcntl(fd,F_SETFD,val);

        pid=fork();
        if(pid==0)
        {
                //子进程中,此描述符并不关闭,仍可使用
                char child_buf[2];
                memset(child_buf,0,sizeof(child_buf) );
                ssize_t bytes = read(fd,child_buf,sizeof(child_buf)-1 );
                printf("child, bytes:%d,%s/n/n",bytes,child_buf);

                //execl执行的程序里,此描述符被关闭,不能再使用它
                char fd_str[5];
                memset(fd_str,0,sizeof(fd_str));
                sprintf(fd_str,"%d",fd);
                int ret = execl("./exe1","exe1", fd_str,NULL);     //把fd作为参数传递给新的进程映像
                if(-1 == ret)
                        perror("ececl fail:");
        }        

        waitpid(pid,NULL,0);
        memset(buffer,0,sizeof(buffer) );
        ssize_t bytes = read(fd,buffer,sizeof(buffer)-1 );
        printf("parent, bytes:%d,%s/n/n",bytes,buffer);
}

jamie@jamie-laptop:~$ cat exe1.c
#include <fcntl.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>

int main(int argc, char **args)
{
        char buffer[20];
        int fd = atoi(args[1]);
        memset(buffer,0,sizeof(buffer) );
        ssize_t bytes = read(fd,buffer,sizeof(buffer)-1);
        if(bytes < 0)
        {
                perror("exe1: read fail:");
                return -1;
        }
        else
        {
                printf("exe1: read %d,%s/n/n",bytes,buffer);
        }
        return 0;
}

jamie@jamie-laptop:~$ gcc -o exe1 exe1.c
jamie@jamie-laptop:~$ gcc -o test test.c
jamie@jamie-laptop:~$ cat wo.txt
this is a test
jamie@jamie-laptop:~$ ./test
3
child, bytes:1,t                               //子进程中可使用fd

exe1: read fail:: Bad file descriptor  //execl调用的程序中不能使用fd
parent, bytes:14,his is a test          //父进程中当然能使用fd


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM