第3章 檔案I O

2021-07-04 19:25:54 字數 4597 閱讀 4493

引言unix系統中的大多數檔案i/o 只需要用到5個函式:open、read、write、lseek以及close

檔案描述符

用於描述檔案開啟或者建立之後返回的乙個非負整數,可以理解為檔案的id

特殊用途的標號: 0:標準輸入、1:標準輸出  、 2:標準錯誤

早期系統一般乙個程序最多開啟16個檔案,現在增加到63個,而有些如mac等可以增加到無限

函式open和openat

呼叫open或openat函式可以開啟或建立乙個檔案

#include int open(const char *path, int oflag,... /* mode_t mode */);   

int openat(int fd, const char *path, int oflag, ... /* mode_t mode */ );

兩函式的返回值:若成功,返回檔案描述符;若出錯,返回−1 

函式creat

也可呼叫creat函式建立乙個新檔案。

#include int creat(const char *path, mode_t mode); 

注意,此函式等效於:

open(path, o_wronly|o_creat|o_trunc, mode);

在早期的unix系統版本中,open的第二個引數只能是0、1或2。無法開啟乙個尚未存在的檔案,因此需要另乙個系統呼叫creat以建立新檔案。現在,open函式提供了選項o_creat和o_trunc,於是也就不再需要單獨的creat函式。

creat的乙個不足之處是它以只寫方式開啟所建立的檔案。在提供open的新版本之前,如果要建立乙個臨時檔案,並要先寫該檔案,然後又讀該檔案,則必須先呼叫creat、close,然後再呼叫open。現在則可用下列方式呼叫open實現:

open(path, o_rdwr|o_creat|o_trunc, mode);
函式close

可呼叫close函式關閉乙個開啟檔案。

#include int close (int fd);
返回值:若成功,返回0;若出錯,返回−1 

關閉乙個檔案時還會釋放該程序加在該檔案上的所有記錄鎖。

當乙個程序終止時,核心自動關閉它所有的開啟檔案。很多程式都利用了這一功能而不顯式地用close關閉開啟檔案。

函式lseek

可以呼叫lseek顯式地為乙個開啟檔案設定偏移量。

#include off_t lseek(int fd, off_t offset, int whence);
返回值:若成功,返回新的檔案偏移量;若出錯,返回為−1 

對引數offset的解釋與引數whence的值有關。

若whence是seek_set,則將該檔案的偏移量設定為距檔案開始處offset個位元組。

若whence是seek_cur,則將該檔案的偏移量設定為其當前值加offset,offset可為正或負。

若whence是seek_end,則將該檔案的偏移量設定為檔案長度加offset,offset可正可負。

函式read

呼叫read函式從開啟檔案中讀資料。

#include ssize_t read(int fd, void *buf, size_t nbytes);
返回值:讀到的位元組數,若已到檔案尾,返回0;若出錯,返回-1 

函式write

呼叫write函式向開啟檔案寫資料。

#include ssize_t write(int fd, const void *buf, size_t nbytes);
返回值:若成功,返回已寫的位元組數;若出錯,返回-1 

i/o的效率

用圖3-5的程式讀檔案,其標準輸出被重新定向到/dev/null上。此測試所用的檔案系統是linux ext4檔案系統,其磁碟塊長度為4096位元組。這也證明了圖3-6中系統cpu時間的幾個最小值差不多出現在buffsize為4?096及以後的位置,繼續增加緩衝區長度對此時間幾乎沒有影響。

大多數檔案系統為改善效能都採用某種預讀(read ahead)技術。當檢測到正進行順序讀取時,系統就試圖讀入比應用所要求的更多資料,並假想應用很快就會讀這些資料。預讀的效果可以從圖3-6中看出,緩衝區長度小至32位元組時的時鐘時間與擁有較大緩衝區長度時的時鐘時間幾乎一樣。

原子操作

1)追加檔案開啟方式是原子操作 (防止多執行緒破壞,如果是先seek在write會出現衝突)

2)呼叫pread相當於呼叫lseek後呼叫read,但是pread又與這種順序呼叫有下列重要區別。

呼叫pread時,無法中斷其定位和讀操作。

不更新當前檔案偏移量。

呼叫pwrite相當於呼叫lseek後呼叫write,但也與它們有類似的區別。

#include ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);   

返回值:讀到的位元組數,若已到檔案尾,返回0;若出錯,返回−1

ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);

返回值:若成功,返回已寫的位元組數;若出錯,返回−1

3)一般而言,原子操作(atomic operation)指的是由多步組成的乙個操作。如果該操作原子地執行,則要麼執行完所有步驟,要麼一步也不執行,不可能只執行所有步驟的乙個子集。

函式dup和dup2

#include int dup(int fd);  

int dup2(int fd, int fd2);

兩函式的返回值:若成功,返回新的檔案描述符;若出錯,返回-1

由dup返回的新檔案描述符一定是當前可用檔案描述符中的最小數值。對於dup2,可以用fd2引數指定新描述符的值。如果fd2已經開啟,則先將其關閉。如若fd等於fd2,則dup2返回fd2,而不關閉它。否則,fd2的fd_cloexec檔案描述符標誌就被清除,這樣fd2在程序呼叫exec時是開啟狀態。

dup(fd); 

等效於

fcntl (fd, f_dupfd, 0);

而呼叫

dup2(fd, fd2);

等效於close(fd2);

fcntl(fd, f_dupfd, fd2);

函式sync、fsync和fdatasync

傳統的unix系統實現在核心中設有緩衝區快取記憶體或頁快取記憶體,大多數磁碟i/o都通過緩衝區進行。當我們向檔案寫入資料時,核心通常先將資料複製到緩衝區中,然後排入佇列,晚些時候再寫入磁碟。這種方式被稱為延遲寫(delayed write)(bach[1986]的第3章詳細討論了緩衝區快取記憶體)。

通常,當核心需要重用緩衝區來存放其他磁碟塊資料時,它會把所有延遲寫資料塊寫入磁碟。為了保證磁碟上實際檔案系統與緩衝區中內容的一致性,unix系統提供了sync、fsync和fdatasync三個函式。

#includeint fsync(int fd);  

int fdatasync(int fd);

返回值:若成功,返回0;若出錯,返回-1

void sync(void);

sync只是將所有修改過的塊緩衝區排入寫佇列,然後就返回,它並不等待實際寫磁碟操作結束。

通常,稱為update的系統守護程序周期性地呼叫(一般每隔30秒)sync函式。這就保證了定期沖洗(flush)核心的塊緩衝區。命令sync(1)也呼叫sync函式。

fsync函式只對由檔案描述符fd指定的乙個檔案起作用,並且等待寫磁碟操作結束才返回。fsync可用於資料庫這樣的應用程式,這種應用程式需要確保修改過的塊立即寫到磁碟上。

fdatasync函式類似於fsync,但它只影響檔案的資料部分。而除資料外,fsync還會同步更新檔案的屬性。

函式fcntl

fcntl函式可以改變已經開啟檔案的屬性。

#includeint fcntl(int fd, int cmd, ... /* int arg */);  

返回值:若成功,則依賴於cmd(見下);若出錯,返回-1

fcntl函式有以下5種功能。

(1)複製乙個已有的描述符(cmd = f_dupfd或f_dupfd_cloexec)。

(2)獲取/設定檔案描述符標誌(cmd = f_getfd或f_setfd)。

(3)獲取/設定檔案狀態標誌(cmd = f_getfl或f_setfl)。

(4)獲取/設定非同步i/o所有權(cmd = f_getown或f_setown)。

(5)獲取/設定記錄鎖(cmd = f_getlk、f_setlk或f_setlkw)

第3章 檔案I O

3.2 檔案描述符 對於核心而言,所有開啟的檔案都通過檔案描述符引用。檔案描述符時乙個非負整數。當開啟乙個現有檔案或建立乙個新檔案時,核心向程序返回乙個檔案描述符。當讀 寫乙個檔案時,使用open或creat返回的檔案描述符標識該檔案,將其作為引數傳送給read或write。按照慣例,unix系統s...

第3章 檔案I O

3.3 函式open和openat 呼叫open或openat函式可以開啟或建立乙個檔案。iinclude int open const char path,int oflag,mode t mode int openat int fd,const char path,int oflag,mode ...

第3章 檔案I O

3.4 函式creat 也可呼叫create函式建立乙個新檔案。include int creat const char path,mode t mode 返回值 若成功,返回為只寫開啟的檔案描述符 若出錯,返回 1 注意,此函式等效於 open path,o wronly o creat o tr...