天天看點

Linux檔案IO(二)标準IO

塊做為檔案系統的抽象,它是I/O 中最基本的概念——所有的磁盤操作都是基于塊進行的。是以,當請求以塊大小整數倍對齊位址時, I/O 效率是最理想的。操作效率随着系統調用次數的增多而急劇下降,例如,每次讀一位元組讀1024次與一次讀1024位元組相比,顯然後者效率更優。如果長度不是block的整數倍,即使每次以大于塊的長度進行一系列的操作,其效率也不是最理想的。例如塊的大小是1K ,每次以1130位元組的長度操作資料要比每次1024位元組的速度慢。

使用者态緩沖I/O

需要對普通檔案執行許多輕量級I/O請求的程式通常使用使用者緩沖I/O。使用者緩沖I/O是在使用者空間而不是在核心中完成的,它可以在程式中設定,也可以調用标準庫透明地執行。實際應用中,塊大小一般是512位元組,1024位元組,2048位元組,或4096位元組。效率的大規模提升隻是通過将每次操作的資料設定為塊大小整數倍或約數獲得的。通過用系統調用stat()可以輕松指定裝置的塊大小。

但問題是程式很少以塊為機關進行操作。程式往往以區域,行,和單個字元為機關進行操作,而不是抽象的塊。如前所述,為了改善這種情況,程式使用使用者緩沖I/O。當資料被寫入時,它會被存儲在程式位址空間的緩沖區中。當緩沖區規模達到一個給定的值(緩沖區大小時),整個緩沖區會在一次操作中被寫出。

C标準庫中提供了标準I/O庫(通常簡單稱作stdio),其中實作了一個跨平台使用者緩沖的解決方案。标準I/O例程并不直接操作檔案描述符。取而代之的是它們用自己唯一的标志符,即大家熟知的檔案指針(filepointer)。在C标準庫裡,檔案指針映射到一個檔案描述符。檔案指針由FILE類型的指針表示,FILE類型定義在<stdio.h>中。在标準I/O中,一個打開的檔案叫做”流”(stream)。流可以被打開用來讀(輸入流),寫(輸出流),或者二者兼有(輸入輸出流)。

打開檔案

FILE fopen(const char path, constchar mode);

FILE fdopen(int fd, const char* mode);

關閉流

int fclose(FILE* stream);

int fcloseall(void);

從流中讀取資料

int fgetc(FILE stream);

int ungetc(int c, FILE stream);

char fgets(char str, int size, FILE stream);

size_t fread(void buf, size_t size, size_t nr, FILE* stream);

向流中寫資料

資料對齊所有的機器設計都有資料對齊的要求。程式員傾向于把記憶體想成一個簡單的位元組數組。但是處理器并不以字元大小對記憶體進行讀寫。相反,處理器以特定的粒度(例如2,4,8或16位元組)來通路記憶體。因為每個處理的位址空間都從0位址開始,程序必須從一個特定粒度的整數倍開始讀取。是以,C變量的存儲和通路都要是位址對齊的。用另一種說法就是一個int需要被存儲在能被4整除的記憶體位址中。通路不對齊的資料在不同的體系結構上有不同程度的性能損失。一些處理器能夠通路不對齊的資料,但是會有一個很大性能損失。有些的處理器根本不能夠通路非對齊的資料,而且企圖這麼做會導緻硬體異常。更糟的是,一些處理器為了強制位址對齊會丢棄了低位的資料,進而導緻不可預料的行為。

int fputc(int c,FILE stream);

int fputs(const char str,FILE stream);

size_t fwrite(void buf, size_t size, size_t nr, FILE* stream);

定位流

int fseek(FILE stream, long offset, int whence);

int fsetpos(FILE stream, fpos_t pos);

int fgetpos(FILE stream, fpos_t pos);

long ftell(FILE stream);

清洗一個流

效率提高的原因——程式保留在使用者空間中,并且運作使用者的代碼,不執行系統調用。隻有當磁盤或其它媒體必須被通路時系統調用才會被執行。fflush()隻是把使用者緩沖的資料寫入到核心緩沖區。效果和沒有使用者緩沖區一樣,而且write()是被直接調用的。但這并不保證資料能夠寫入實體媒體——如果需要的話,使用fsync()這一類函數。

int fflush(FILE* stream); // 将使用者緩沖區寫入核心

錯誤和檔案結束

一些标準I/O接口,例如fread(),向調用者傳遞失敗資訊的能力很差,因為它們沒有提供區分錯誤和EOF的機制。在一些場合中調用這些函數時,需要區分給定的流出現了錯誤還是到達了檔案結尾。标準I/O為此提供了兩個函數。函數ferror()測試是否在流上設定了錯誤标志:

int ferror(FILE stream);

int feof(FILE stream);

void clearerr(FILE* stream);

獲得關聯的檔案描述符

int fileno(FILE* stream);

控制緩沖

标準I/O實作了三種使用者緩沖,而且為開發者提供了一個用來控制緩沖區大小和類型的接口。不同的使用者緩沖提供不同功能,并适用于不同的場合。下面是一些選項:

不緩沖 沒有執行使用者緩沖。資料直接送出到核心。因為這和使用者緩沖對立,這個選項通常不用。标準錯誤預設是不緩沖的。

行緩沖 緩沖以行為機關執行。每當遇到換行符,緩沖區被送出到核心。行緩沖對輸出到螢幕的流有用。是以,它是終端的預設緩沖方式(标準輸出預設為行緩沖)。

塊緩沖 緩沖以塊為機關執行。這是本章一開始讨論的緩沖類型,而且它适用于檔案。預設的所有和檔案相關的流都是塊緩沖的。标準I/O稱塊緩沖為全緩沖。

setbuf()函數設定流的緩沖類型模式:

int setvbuf(FILE stream, char buf,int mode,size_t size);

線程安全

線程就是在同一個程序中執行的多個執行個體。線程的定義是共享同一位址空間的多個程序。如果不采取資料同步措施或将資料線程私有化,線程可以任何時間修改共享資料。支援線程的作業系統提供加鎖機制(保證互相排斥的程式結構)來保證線程不會互相幹擾。标準I/O使用這些機制。而且,這些機制通常還不能滿足需求。例如,有時候你想給一組調用加鎖,将臨界區(一段獨立運作的代碼)的範圍從一個I/O操作擴大到幾個。而有些情況下,你可能想取消鎖機制來提高效率。标準I/O的函數本質上是線程安全的。

POSIX費線程安全函數:

http://kimi.it/506.html

标準I/O為獨立操縱和流關聯的鎖提供了一系列的函數。通常,取消鎖會導緻各種各樣的問題。但一些程式可能顯式地将所有的I/O操作都由一個線程來完成。在這種情況下,沒有必要增加鎖的開銷。

void flockfile(FILE stream);

void funlockfile(FILE stream);

int ftrylockfile(FILE stream);

Linux提供了一系列的函數,類似于通常的标準I/O接口,但是不執行任何鎖操作。他們實際上是不加鎖的标準I/O:除了它們不檢查或獲得指定流上的鎖,這些函數與相對的加鎖函數執行相同操作。

fgetc_unlocked、fgets_unlocked、fread_unlocked、fputc_unlocked、fputs_unlocked、fwrite_unlocked、fflush_unlocked、feof_unlocked、ferror_unlocked、fileno_unlocked、clearerr_unlocked

繼續閱讀