春天來了,除了工作學習,大家也要注意鍛煉身體,多出去運動運動。?
上周末在元大都遺址公園海棠花溪拍的海棠花。
?
進入正題。
O_DIRECT和O_SYNC是系統調用open的flag參數。通過指定open的flag參數,以特定的文件描述符打開某一文件。
這兩個flag會對寫盤的性能有很大的影響,因此對這兩個flag做一些詳細的了解。
先看一個open函數的使用例子.
/* Open new or existing file for reading and wrting,
sync io and no buffer io; file permissions read+
write for owner, nothing for all others */
fd = open("myfile", O_RDWR | O_CREAT | O_SYNC | O_DIRECT, S_IRUSR | S_IWUSR);
if (fd == -1)
errExit("open");
?
O_DIRECT: 無緩沖的輸入、輸出。
O_SYNC:以同步IO方式打開文件。
下面對這兩個flag做一些詳細的說明。
?
一,O_DIRECT,繞過緩沖區高速緩存,直接IO
直接IO:Linux允許應用程序在執行磁盤IO時繞過緩沖區高速緩存,從用戶空間直接將數據傳遞到文件或磁盤設備,稱為直接IO(direct IO)或者裸IO(raw IO)。
應用場景:數據庫系統,其高速緩存和IO優化機制均自成一體,無需內核消耗CPU時間和內存去完成相同的任務。
使用直接IO的弊端:可能會大大降低性能,內核對緩沖區告訴緩存做了不少優化,包括:按順序預讀取,在成簇磁盤塊上執行IO,允許訪問同一文件的多個進程共享高速緩存的緩沖區。
使用方法:在調用open函數打開文件或設備時指定O_DIRECT標志。
注意可能發生的不一致性:若一進程以O_DIRECT標志打開某文件,而另一進程以普通(即使用了高速緩存緩沖區)打開同一文件,則由直接IO所讀寫的數據與緩沖區高速緩存中內容之間不存在一致性,應盡量避免這一場景。
?
使用直接IO需要遵守的一些限制:
- 用於傳遞數據的緩沖區,其內存邊界必須對齊為塊大小的整數倍
- 數據傳輸的開始點,即文件和設備的偏移量,必須是塊大小的整數倍
- 待傳遞數據的長度必須是塊大小的整數倍。
不遵守上述任一限制均將導致EINVAL錯誤。
?
二,O_SYNC,以同步方式寫入文件
功能:強制刷新內核緩沖區到輸出文件。這是有必要的,因為為了數據安全,需要確保將數據真正寫入磁盤或者磁盤的硬件告訴緩存中。
我們先熟悉一下同步IO相關定義和系統調用。
同步IO數據完整性和同步IO文件完整性
同步IO的定義:某一IO操作,要麼已成功完成到磁盤的數據傳遞,要麼被診斷為不成功。
SUSv3定義的兩種同步IO完成類型(此處用英文,因為譯者也忍無可忍用了原文…)
- synchronized IO data integrity completion:確保針對文件的一次更新傳遞了足夠的信息(部分文件元數據)到磁盤,以便於之後對數據的獲取。
- synchronized IO file integrity completion:確保針對文件的一次更新傳遞了所有的信息(所有文件元數據)到磁盤,即使有些在後續對文件數據的操作並不需要。
用於控制文件IO內核緩沖的系統調用
1 fsync
作用:fsync()系統調用將使緩沖數據和fd相關的所有元數據都刷新到磁盤上。調用fsync會強制使文件處於Synchronized IO file integrity completion狀態。
函數聲明:
#include
int fsync(int fd);
?函數返回值:
返回時間:僅在對磁盤設備(或者至少是其高速緩存)的傳遞完成後,fsync()調用才會返回。
?
2 fdatasync
作用:fdatasync()系統調用的作用類似fsync(),只是強制文件處於synchronized IO data integrity compeletion狀態。
函數聲明:
#include
int fdatasync(int fd);
?函數返回值:
與fsync的區別:fdatasync()可能會減少磁盤操作的次數,由fsync()調用請求的兩次變成一次。例如,修改了文件的數據,而文件大小不變,那麼調用fdatasync調用請求只強制進行了數據更新,相比之下,fsync()調用會強制將元數據傳遞到磁盤上,而元數據和文件數據通常駐留在磁盤的不同區域,更新這些數據需要反復在整個磁盤上執行尋道操作。
?
3 sync系統調用
作用:sync()系統調用會使包含更新文件信息的所有內核緩沖區(即數據塊、指針塊、元數據等)刷新到磁盤上。
函數聲明:
#include
void sync(void);
?細節:若內容發生變化的內核緩沖區在30s內未經顯式方式同步到磁盤上,則一條長期運行的內核線程會確保將其刷新到磁盤上。這一做法是為了規避緩沖區與相關磁盤文件內容長期處於不一致狀態。
?
4 使所有寫入同步:O_SYNC
調用open()函數時,如制定O_SYNC標志,則會使所有後續輸出同步。
fd = open(pathname, O_WRONLY | O_SYNC);
?作用:調用open後,每個write調用會自動將文件數據和元數據刷新到磁盤上,即按照Synchronized IO file integrity completion的要求執行寫操作。
?
5 有無O_SYNC性能對比
場景:將一百萬字節寫入一個ext2文件系統上的新創建文件,比較寫入時間。
對比結果:
從結果中可以得到的結論:
- 采用O_SYNC標志(或者頻繁調用fsync(), fdatasync()或sync())對性能影響極大。
- 性能下降的直接表現為運行總用時大為增加:在緩沖區為1字節的情況下,運行時間相差1000多倍。
- 以O_SYNC標志執行寫操作時運行總用時和CPU時間之間的巨大差異(1030 - 98.8),原因是系統在每個緩沖區中將數據向磁盤傳遞時會把程序阻塞起來。
?
?三,IO緩沖層次關系
先總結一下stdio函數庫和內核采用的緩沖這兩級緩沖,然後用圖說明兩層緩沖機制和各種緩沖類型的控制機制。
- 首先,通過stdio庫將用戶數據傳遞到stdio緩沖區,該緩沖區位於用戶態內存區。
- 當緩沖區填滿,stdio庫會調用write()系統調用,將數據傳遞到內核高速緩沖區,該緩沖區位於內核態內存區。
- 最終,內核發起磁盤操作。
該層次結構如下圖所示
?
?
上圖中,左側虛線方框中為可於任何時刻顯式強制刷新各類緩沖區的調用。
右側所示為促使刷新自動化的調用:通過禁用stdio的緩沖,和在文件輸出類的系統調用中啟用同步,從而使每個write()調用立刻刷新到磁盤。
?
四,小結
輸入輸出數據的緩沖由內核和stdio庫完成。有時可能希望阻止緩沖,但這需要了解其對應用程序性能的影響。
可以使用各種系統調用和庫函數來控制內核和stdio緩沖,並執行一次性的緩沖區刷新。
在Linux環境下,open()所特有的O_DIRECT標識允許特定應用跳過緩沖區高速緩存。
?
?
雖然題目還是UNIX高級環境變成(xx),但是打算把所閱讀和參考的書換成《Linux/UNIX系統編程手冊》。感覺這本書內容更新一點。
工作很忙,周末大部分時間都在外面活動,跑步拍照,雖然只是簡單的讀書這一篇也是拖了又拖才敲完。
?
參考:
《Linux/UNIX系統編程手冊(上冊)》 ?