1. 程式人生 > >open和close

open和close

fopen(3)
呼叫open(2)開啟指定的檔案,返回一個檔案描述符(就是一個int型別的編號),分配一個FILE結構體,其中包含該檔案的描述符、I/O緩衝區和當前讀寫位置等資訊,返回這個FILE結構體的地址。
fgetc(3)
通過傳入的FILE *引數找到該檔案的描述符、I/O緩衝區和當前讀寫位置,判斷能否從I/O緩衝區中讀到下一個字元,如果能讀到就直接返回該字元,否則呼叫read(2),把檔案描述符傳進去,讓核心讀取該檔案的資料到I/O緩衝區,然後返回下一個字元。注意,對於C標準I/O庫來說,開啟的檔案由FILE *指標標識,而對於核心來說,開啟的檔案由檔案描述符標識,檔案描述符從open系統呼叫獲得,在使用read、write、close系統呼叫時都需要傳檔案描述符。
fputc(3)
判斷該檔案的I/O緩衝區是否有空間再存放一個字元,如果有空間則直接儲存在I/O緩衝區中並返回,如果I/O緩衝區已滿就呼叫write(2),讓核心把I/O緩衝區的內容寫回檔案。
fclose(3)

如果I/O緩衝區中還有資料沒寫回檔案,就呼叫write(2)寫回檔案,然後呼叫close(2)關閉檔案,釋放FILE結構體和I/O緩衝區。

open、read、write、close等系統函式稱為無緩衝I/O(Unbuffered I/O)函式,因為它們位於C標準庫的I/O緩衝區的底層。

用Unbuffered I/O函式每次讀寫都要進核心,調一個系統呼叫比調一個使用者空間的函式要慢很多,所以在使用者空間開闢I/O緩衝區還是必要的,用C標準I/O庫函式就比較方便,省去了自己管理I/O緩衝區的麻煩。用C標準I/O庫函式要時刻注意I/O緩衝區和實際檔案有可能不一致,在必要時需呼叫fflush(3)。我們知道UNIX的傳統是Everything is a file,I/O函式不僅用於讀寫常規檔案,也用於讀寫裝置,比如終端或網路裝置。在讀寫裝置時通常是不希望有緩衝的,例如向代表網路裝置的檔案寫資料就是希望資料通過網路裝置傳送出去,而不希望只寫到緩衝區裡就算完事兒了,當網路裝置接收到資料時應用程式也希望第一時間被通知到,所以網路程式設計通常直接呼叫Unbuffered I/O函式。

#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

pathname引數是要開啟或建立的檔名,和fopen一樣,pathname既可以是相對路徑也可以是絕對路徑。flags引數有一系列常數值可供選擇,可以同時選擇多個常數用按位或運算子連線起來,所以這些常數的巨集定義都以O_開頭,表示or。
必選項:以下三個常數中必須指定一個,且僅允許指定一個。

O_RDONLY 只讀開啟

O_WRONLY 只寫開啟

O_RDWR 可讀可寫開啟
以下可選項可以同時指定0個或多個,和必選項按位或起來作為flags引數。可選項有很多,這裡只介紹一部分,其它選項可參考open(2)的Man Page:

O_APPEND 表示追加。如果檔案已有內容,這次開啟檔案所寫的資料附加到檔案的末尾而不覆蓋原來的內容。

O_CREAT 若此檔案不存在則建立它。使用此選項時需要提供第三個引數mode,表示該檔案的訪問許可權。

O_EXCL 如果同時指定了O_CREAT,並且檔案已存在,則出錯返回。

O_TRUNC 如果檔案已存在,並且以只寫或可讀可寫方式開啟,則將其長度截斷(Truncate)為0位元組。
524
O_NONBLOCK 對於裝置檔案,以O_NONBLOCK方式開啟可以做非阻塞I/O(Nonblock I/O)。
注意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的mode引數和當前程序的umask掩碼共同決定。

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就不會列印到螢幕上,而是寫到這個檔案中了。後