標籤:open o-cloexec
關於open函數O_CLOEXEC模式,fcntl函數FD_CLOEXEC選項,總結為如下幾點:
調用open函數O_CLOEXEC模式開啟的檔案描述符在執行exec調用新程式中關閉,且為原子操作。
調用open函數不使用O_CLOEXEC模式開啟的檔案描述符,然後調用fcntl 函數設定FD_CLOEXEC選項,效果和使用O_CLOEXEC選項open函數相同,但分別調用open、fcnt兩個l函數,不是原子操作,多線程環境中存在競態條件,故用open函數O_CLOEXEC選項代替之。
調用open函數O_CLOEXEC模式開啟的檔案描述符,或是使用fcntl設定FD_CLOEXEC選項,這二者得到(處理)的描述符在通過fork調用產生的子進程中均不被關閉。
調用dup族類函數得到的新檔案描述符將清除O_CLOEXEC模式。
測試程式如下:
#include <unistd.h>#include <sys/types.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#define err_sys(fmt, arg...) \do { printf(fmt, ##arg); printf("\nerrno:%d %s\n", errno, strerror(errno)); exit(EXIT_FAILURE);} while (0)int main(){ int fd, fd2, val; pid_t pid;#ifdef _O_CLOEXEC if ((fd = open("my.txt", O_RDWR | O_CREAT | O_CLOEXEC, 0600)) < 0) #else if ((fd = open("my.txt", O_RDWR | O_CREAT, 0600)) < 0) #endif err_sys("open error");#ifdef _DUP if ((fd2 = dup(fd)) < 0) err_sys("dup error"); if (write(fd2, "123", 3) < 0) err_sys("write error");#endif#ifdef _FCNTL_CLOEXEC if ((val = fcntl(fd, F_GETFD)) < 0) err_sys("fcntl(F_GETFD) error"); val |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, val) < 0) err_sys("fcntl( F_SETFD) error");#endif#ifndef _FORK if (execl("/bin/sleep", "sleep", "10000", (void*)0) < 0) err_sys("execl error");#else switch ((pid = fork())) { case -1: err_sys("fork error"); case 0: sleep(10000); break; default: sleep(10000); break; }#endif return 0;}
通過宏_O_CLOEXEC編譯進O_CLOEXEC選項
#gcc -D_O_CLOEXEC -o cloexec cloexec.c
執行程式
#./cloexec
查看進程和進程資源,注意執行execl後進程名字變為sleep了
#ps aux|grep sleep root 7900 0.0 0.0 4200 280 pts/0 S+ 11:45 0:00 sleep 10000root 7915 0.0 0.1 4384 836 pts/1 S+ 11:49 0:00 grep --color=auto sleep#lsof -p 7900 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsleep 7900 root cwd DIR 8,1 4096 163741 /home/zozy/AUPsleep 7900 root rtd DIR 8,1 4096 2 /sleep 7900 root txt REG 8,1 26156 131621 /bin/sleepsleep 7900 root mem REG 8,1 2919792 12 /usr/lib/locale/locale-archivesleep 7900 root mem REG 8,1 1730024 15701 /lib/i386-linux-gnu/libc-2.15.sosleep 7900 root mem REG 8,1 134344 15713 /lib/i386-linux-gnu/ld-2.15.sosleep 7900 root 0u CHR 136,0 0t0 3 /dev/pts/0sleep 7900 root 1u CHR 136,0 0t0 3 /dev/pts/0sleep 7900 root 2u CHR 136,0 0t0 3 /dev/pts/0
可以看出進程資源中沒有檔案my.txt
不帶宏_O_CLOEXEC編譯
#gcc -o nocloexec cloexec.c
執行程式
#./nocloexec
查看進程和進程資源
#ps aux|grep sleeproot 7925 0.0 0.0 4200 280 pts/0 S+ 11:51 0:00 sleep 10000root 7928 0.0 0.1 4384 836 pts/1 S+ 11:52 0:00 grep --color=auto sleep#lsof -p 7925COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsleep 7925 root cwd DIR 8,1 4096 163741 /home/zozy/AUPsleep 7925 root rtd DIR 8,1 4096 2 /sleep 7925 root txt REG 8,1 26156 131621 /bin/sleepsleep 7925 root mem REG 8,1 2919792 12 /usr/lib/locale/locale-archivesleep 7925 root mem REG 8,1 1730024 15701 /lib/i386-linux-gnu/libc-2.15.sosleep 7925 root mem REG 8,1 134344 15713 /lib/i386-linux-gnu/ld-2.15.sosleep 7925 root 0u CHR 136,0 0t0 3 /dev/pts/0sleep 7925 root 1u CHR 136,0 0t0 3 /dev/pts/0sleep 7925 root 2u CHR 136,0 0t0 3 /dev/pts/0sleep 7925 root 3u REG 8,1 0 163759 /home/zozy/AUP/my.txt
可以看出進程資源中有檔案my.txt
測試fcntl函數可以通過設定編譯宏-D_FCNTL_CLOEXEC測試,編譯測試過程同上,同理通過開啟-D_O_CLOEXEC -D_FORK編譯選項測試使用O_CLOEXEC模式的描述符在子進程中的狀態,通過宏-D_DUP編譯選項測試dup函數對O_CLOEXEC的影響,編譯測試過程略。
淺析open函數O_CLOEXEC模式和fcntl函數FD_CLOEXEC選項