man open里有这么一个flag:
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).
意思就是新的内核里的这个选项是把fcntl的这个设置放在open里原子操作,以免在多线程程序里有可能会出现fcntl在设置的同时其它线程在fork+execve,虽然在线程里fork比较罕见.这个选项的意思就是子进程默认是继承父进程打开的所有fd,如果句柄加入了这个设置,在execve替换进程时就会关闭设置这个选项的所有fd.写个例子:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <sys/wait.h> 4 #include <unistd.h> 5 #include <fcntl.h> 6 #include <stdio.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <pthread.h> 10 11 int flag=0; 12 void* threadFunc(void* arg) 13 { 14 #ifdef _CLOEXEC_ 15 int fd = open("mm.log", O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, 0644); 16 #else 17 int fd = open("mm.log", O_CREAT|O_WRONLY|O_TRUNC, 0644); 18 #endif 19 printf("fd for mm.log is : %d\n", fd); 20 if(fd<0) { perror("open mm.log failed"); exit(-1);} 21 22 char s[32]=""; 23 sprintf(s, "%u", getpid()); 24 write(fd, s, strlen(s)); 25 flag=1; 26 sleep(1000); 27 } 28 29 int main(int argc, char* argv[]) 30 { 31 pthread_t thd; 32 pthread_create(&thd, NULL, threadFunc, NULL); 33 34 while(!flag); 35 36 pid_t pid; 37 pid=fork(); 38 if(pid<0) { perror("fork failed"); exit(-1);} 39 else if(pid==0) 40 { 41 //child 42 if(execl("/bin/sleep", "sleep", "100")== -1) { perror("exit failed"); exit(-1);} 43 44 _exit(0); 45 } 46 printf("child pid: %u\n", pid); 47 exit(0); 48 }
使用两个选项编译两个程序:
[libgcc@Heineken ~/cpp]$ gcc -U_CLOEXEC_ -o nocloexec o_cloexec.c -lpthread [libgcc@Heineken ~/cpp]$ gcc -D_CLOEXEC_ -o cloexec o_cloexec.c -lpthread [libgcc@Heineken ~/cpp]$ ./nocloexec fd for mm.log is : 3 child pid: 2201 [libgcc@Heineken ~/cpp]$ ps -ef|grep 220[1] libgcc 2201 1 0 03:23 pts/0 00:00:00 sleep 100 [libgcc@Heineken ~/cpp]$ lsof -p 2201 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sleep 2201 libgcc cwd DIR 253,0 4096 319493 /home/libgcc/cpp sleep 2201 libgcc rtd DIR 253,0 4096 2 / sleep 2201 libgcc txt REG 253,0 25688 262402 /bin/sleep sleep 2201 libgcc mem REG 253,0 151500 677607 /lib/ld-2.12.90.so sleep 2201 libgcc mem REG 253,0 1889628 677608 /lib/libc-2.12.90.so sleep 2201 libgcc mem REG 253,0 99158720 399306 /usr/lib/locale/locale-archive sleep 2201 libgcc 0u CHR 136,0 0t0 3 /dev/pts/0 sleep 2201 libgcc 1u CHR 136,0 0t0 3 /dev/pts/0 sleep 2201 libgcc 2u CHR 136,0 0t0 3 /dev/pts/0 sleep 2201 libgcc 3w REG 253,0 4 336396 /home/libgcc/cpp/mm.log [libgcc@Heineken ~/cpp]$ ./cloexec fd for mm.log is : 3 child pid: 2216 [libgcc@Heineken ~/cpp]$ ps -ef|grep 221[6] libgcc 2216 1 0 03:24 pts/0 00:00:00 sleep 100 [libgcc@Heineken ~/cpp]$ lsof -p 2216 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sleep 2216 libgcc cwd DIR 253,0 4096 319493 /home/libgcc/cpp sleep 2216 libgcc rtd DIR 253,0 4096 2 / sleep 2216 libgcc txt REG 253,0 25688 262402 /bin/sleep sleep 2216 libgcc mem REG 253,0 151500 677607 /lib/ld-2.12.90.so sleep 2216 libgcc mem REG 253,0 1889628 677608 /lib/libc-2.12.90.so sleep 2216 libgcc mem REG 253,0 99158720 399306 /usr/lib/locale/locale-archive sleep 2216 libgcc 0u CHR 136,0 0t0 3 /dev/pts/0 sleep 2216 libgcc 1u CHR 136,0 0t0 3 /dev/pts/0 sleep 2216 libgcc 2u CHR 136,0 0t0 3 /dev/pts/0
看到没,如果不加这个选项,sleep程序都能打开一个文本文件.
浙公网安备 33010602011771号