open
函數可以打開或創建一個檔案。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); 返回值:成功返回新分配的檔案描述符,出錯返回-1並設置errno
在Man Page中open
函數有兩種形式,一種帶兩個參數,一種帶三個參數,其實在C代碼中open
函數是這樣聲明的:
int open(const char *pathname, int flags, ...);
最後的可變參數可以是0個或1個,由flags
參數中的標誌位決定,見下面的詳細說明。
pathname
參數是要打開或創建的檔案名,和fopen
一樣,pathname
既可以是相對路徑也可以是絶對路徑。flags
參數有一系列常數值可供選擇,可以同時選擇多個常數用按位或運算符連接起來,所以這些常數的宏定義都以O_
開頭,表示or。
必選項:以下三個常數中必須指定一個,且僅允許指定一個。
O_RDONLY
只讀打開
O_WRONLY
只寫打開
O_RDWR
可讀可寫打開
以下可選項可以同時指定0個或多個,和必選項按位或起來作為flags
參數。可選項有很多,這裡只介紹一部分,其它選項可參考open(2)
的Man Page:
注意open
函數與C標準I/O庫的fopen
函數有些細微的區別:
以可寫的方式fopen
一個檔案時,如果檔案不存在會自動創建,而open
一個檔案時必須明確指定O_CREAT
才會創建檔案,否則檔案不存在就出錯返回。
以w
或w+
方式fopen
一個檔案時,如果檔案已存在就截斷為0位元組,而open
一個檔案時必須明確指定O_TRUNC
才會截斷檔案,否則直接在原來的數據上改寫。
第三個參數mode
指定檔案權限,可以用八進制數表示,比如0644表示-rw-r--r--
,也可以用S_IRUSR
、S_IWUSR
等宏定義按位或起來表示,詳見open(2)
的Man Page。要注意的是,檔案權限由open
的mode
參數和當前進程的umask
掩碼共同決定。
補充說明一下Shell的umask
命令。Shell進程的umask
掩碼可以用umask
命令查看:
$ umask 0022
用touch
命令創建一個檔案時,創建權限是0666,而touch
進程繼承了Shell進程的umask
掩碼,所以最終的檔案權限是0666&~022=0644。
$ touch file123 $ ls -l file123 -rw-r--r-- 1 akaedu akaedu 0 2009-03-08 15:07 file123
同樣道理,用gcc
編譯生成一個執行檔時,創建權限是0777,而最終的檔案權限是0777&~022=0755。
$ gcc main.c $ ls -l a.out -rwxr-xr-x 1 akaedu akaedu 6483 2009-03-08 15:07 a.out
我們看到的都是被umask
掩碼修改之後的權限,那麼如何證明touch
或gcc
創建檔案的權限本來應該是0666和0777呢?我們可以把Shell進程的umask
改成0,再重複上述實驗:
$ umask 0 $ touch file123 $ rm file123 a.out $ touch file123 $ ls -l file123 -rw-rw-rw- 1 akaedu akaedu 0 2009-03-08 15:09 file123 $ gcc main.c $ ls -l a.out -rwxrwxrwx 1 akaedu akaedu 6483 2009-03-08 15:09 a.out
現在我們自己寫一個程序,在其中調用open("somefile", O_WRONLY|O_CREAT, 0664);
創建檔案,然後在Shell中運行並查看結果:
$ umask 022 $ ./a.out $ ls -l somefile -rw-r--r-- 1 akaedu akaedu 6483 2009-03-08 15:11 somefile
不出所料,檔案somefile
的權限是0664&~022=0644。有幾個問題現在我沒有解釋:為什麼被Shell啟動的進程可以繼承Shell進程的umask
掩碼?為什麼umask
命令可以讀寫Shell進程的umask
掩碼?這些問題將在第 1 節 “引言”解釋。
close
函數關閉一個已打開的檔案:
#include <unistd.h> int close(int fd); 返回值:成功返回0,出錯返回-1並設置errno
參數fd
是要關閉的檔案描述符。需要說明的是,當一個進程終止時,內核對該進程所有尚未關閉的檔案描述符調用close
關閉,所以即使用戶程序不調用close
,在終止時內核也會自動關閉它打開的所有檔案。但是對於一個長年累月運行的程序(比如網絡伺服器),打開的檔案描述符一定要記得關閉,否則隨着打開的檔案越來越多,會占用大量檔案描述符和系統資源。
由open
返回的檔案描述符一定是該進程尚未使用的最小描述符。由於程序啟動時自動打開檔案描述符0、1、2,因此第一次調用open
打開檔案通常會返回描述符3,再調用open
就會返回4。可以利用這一點在標準輸入、標準輸出或標准錯誤輸出上打開一個新檔案,實現重定向的功能。例如,首先調用close
關閉檔案描述符1,然後調用open
打開一個常規檔案,則一定會返回檔案描述符1,這時候標準輸出就不再是終端,而是一個常規檔案了,再調用printf
就不會打印到屏幕上,而是寫到這個檔案中了。後面要講的dup2
函數提供了另外一種辦法在指定的檔案描述符上打開檔案。