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就能實現,很簡單幾句代碼就能實現:
- 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標記 [注意:linux 2.6.23才開始支持此標記]
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