open函数的O_CLOEXEC参数官方文档给的解释是:
O_CLOEXEC (Since Linux 2.6.23)
Enable the close-on-exec flag for the new file descriptor. Specifying this flag permits a program to avoid additional fcntl(2) F_SETFD operations to
set the FD_CLOEXEC flag. Additionally, use of this flag is essential in some multithreaded programs since using a separate fcntl(2) F_SETFD operation
to set the FD_CLOEXEC flag does not suffice to avoid race conditions where one thread opens a file descriptor at the same time as another thread does a
fork(2) plus execve(2).
翻译过来主要意思是:在创建时包含该flag,可以防止竞争的产生
open函数官方文档中有这么一句话:
By default, the new file descriptor is set to remain open across an execve(2) (i.e., the FD_CLOEXEC file descriptor flag described in fcntl(2) is initially
disabled; the O_CLOEXEC flag, described below, can be used to change this default). The file offset is set to the beginning of the file (see lseek(2)).
翻译过来主要意思是:FD_CLOEXEC默认是关闭的,文件描述符在fork或者fork+execve之后是默认打开的,具体点就是该文件被打开的引用计数+1了。
下面就使用代码验证下该功能:
vim cloexec.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "unistd.h"
#include "string.h"
#include "errno.h"
#include "stdio.h"
#include "stdlib.h"
#include <sys/prctl.h>
int main(int argc, char *argv[])
{
printf("there is %d param\n",argc);
//int fd=open("/home/wade/test/a.txt",O_APPEND|O_RDWR|O_CLOEXEC);
int fd=open("/home/wade/test/a.txt",O_APPEND|O_RDWR);
if(fd == -1)
{
printf("open failed and reason is %s\n",strerror(errno));
}
printf("fd is %d\n",fd);
pid_t expid=fork();
if(expid == 0)
{
char fdptr[5];
memset(fdptr,0,5);
sprintf(fdptr,"%d",fd);
char* execargv[]={fdptr,NULL};
char *newenviron[] = { NULL };
int exret = execve("./print",execargv,newenviron);
if(exret == -1)
printf("execve returned and error is %s\n",strerror(errno));
}
printf("children pid is %d \n",expid);
char c_write[2]="a";
{
int wret = write(fd,&c_write,2);
if(wret==-1)
{
printf("master write %d to a.txt and reason is %s\n",wret,strerror(errno));
}
else
{
printf("master write %d to a.txt \n",wret);
}
sleep(2);
}
}
vim print.c
#include "errno.h"
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "string.h"
int main(int argc, char *argv[], char *envp[])
{
while(1)
{
int fd= atoi(argv[0]);
printf("printf get fd is %d\n",fd);
ssize_t write_ret = write(fd,"execve write",sizeof("execve write"));
if(write_ret == -1)
{
printf("execve write %ld reason is %s\n",write_ret,strerror(errno));
}
else
{
printf("execve write %ld\n",write_ret);
}
sleep(2);
}
return 0;
}
编译并运行:
gcc -Wall -g cloexec.c -o cloexe
gcc -Wall -g print.c -o print
查看结果
there is 1 param
fd is 3
children pid is 27813
master write 2 to a.txt
printf get fd is 3
execve write 13
master write 2 to a.txt
printf get fd is 3
execve write 13
master write 2 to a.txt
printf get fd is 3
execve write 13
master write 2 to a.txt
printf get fd is 3
execve write 13
如果把O_CLOEXEC标志位加上:
重新编译运行
结果为:
there is 1 param
fd is 3
children pid is 27822
master write 2 to a.txt
printf get fd is 3
execve write -1 reason is Bad file descriptor
master write 2 to a.txt
printf get fd is 3
execve write -1 reason is Bad file descriptor
master write 2 to a.txt
printf get fd is 3
execve write -1 reason is Bad file descriptor
可以看到文件描述符是关闭的