linux 同步IO: sync msync、fsync、fdatasync與 fflush

最近閱讀leveldb源碼,作為一個保證可靠性的kv數據庫其數據與磁盤的交互可謂是極其關鍵,其中涉及到了不少內存和磁盤同步的操作和策略。為了加深理解,從網上整理了linux池畔同步IO相關的函數,這里做一個羅列和對比。大部分為copy,僅為記錄,請各位看官勿噴。

傳統的UNIX實現在內核中設有緩沖區高速緩存或頁面高速緩存,大多數磁盤I/O都通過緩沖進行。當將數據寫入文件時,內核通常先將該數據復制到其中一個緩沖區中,如果該緩沖區尚未寫滿,則并不將其排入輸出隊列,而是等待其寫滿或者當內核需要重用該緩沖區以便存放其他磁盤塊數據時,再將該緩沖排入輸出隊列,然后待其到達隊首時,才進行實際的I/O操作。這種輸出方式被稱為延遲寫(delayed write)(Bach [1986]第3章詳細討論了緩沖區高速緩存)。
延遲寫減少了磁盤讀寫次數,但是卻降低了文件內容的更新速度,使得欲寫到文件中的數據在一段時間內并沒有寫到磁盤上。當系統發生故障時,這種延遲可能造成文件更新內容的丟失。為了保證磁盤上實際文件系統與緩沖區高速緩存中內容的一致性,UNIX系統提供了sync、fsync和fdatasync三個函數。
sync函數只是將所有修改過的塊緩沖區排入寫隊列,然后就返回,它并不等待實際寫磁盤操作結束。
通常稱為update的系統守護進程會周期性地(一般每隔30秒)調用sync函數。這就保證了定期沖洗內核的塊緩沖區。命令sync(1)也調用sync函數。
fsync函數只對由文件描述符filedes指定的單一文件起作用,并且等待寫磁盤操作結束,然后返回。fsync可用于數據庫這樣的應用程序,這種應用程序需要確保將修改過的塊立即寫到磁盤上。
fdatasync函數類似于fsync,但它只影響文件的數據部分。而除數據外,fsync還會同步更新文件的屬性。?

對于提供事務支持的數據庫,在事務提交時,都要確保事務日志(包含該事務所有的修改操作以及一個提交記錄)完全寫到硬盤上,才認定事務提交成功并返回給應用層。?

一個簡單的問題:在*nix操作系統上,怎樣保證對文件的更新內容成功持久化到硬盤??

1.? write不夠,需要fsync

一般情況下,對硬盤(或者其他持久存儲設備)文件的write操作,更新的只是內存中的頁緩存(page cache),而臟頁面不會立即更新到硬盤中,而是由操作系統統一調度,如由專門的flusher內核線程在滿足一定條件時(如一定時間間隔、內存中的臟頁達到一定比例)內將臟頁面同步到硬盤上(放入設備的IO請求隊列)。?

因為write調用不會等到硬盤IO完成之后才返回,因此如果OS在write調用之后、硬盤同步之前崩潰,則數據可能丟失。雖然這樣的時間窗口很小,但是對于需要保證事務的持久化(durability)和一致性(consistency)的數據庫程序來說,write()所提供的“松散的異步語義”是不夠的,通常需要OS提供的同步IO(synchronized-IO)原語來保證:

int fsync(int fd);

fsync的功能是確保文件fd所有已修改的內容已經正確同步到硬盤上,該調用會阻塞等待直到設備報告IO完成。

PS:如果采用內存映射文件的方式進行文件IO(使用mmap,將文件的page cache直接映射到進程的地址空間,通過寫內存的方式修改文件),也有類似的系統調用來確保修改的內容完全同步到硬盤之上:

int msync(void *addr, size_t length, int flags)

msync需要指定同步的地址區間,如此細粒度的控制似乎比fsync更加高效(因為應用程序通常知道自己的臟頁位置),但實際上(Linux)kernel中有著十分高效的數據結構,能夠很快地找出文件的臟頁,使得fsync只會同步文件的修改內容。

2. fsync的性能問題,與fdatasync

除了同步文件的修改內容(臟頁),fsync還會同步文件的描述信息(metadata,包括size、訪問時間st_atime & st_mtime等等),因為文件的數據和metadata通常存在硬盤的不同地方,因此fsync至少需要兩次IO寫操作,fsync的man page這樣說:

"Unfortunately fsync() will always initialize two write operations : one for the newly written data and another one in order to update the modification time stored in the inode. If the modification time is not a part of the transaction concept fdatasync() can be used to avoid unnecessary inode disk write operations."

?

多余的一次IO操作,有多么昂貴呢?根據Wikipedia的數據,當前硬盤驅動的平均尋道時間(Average seek time)大約是3~15ms,7200RPM硬盤的平均旋轉延遲(Average rotational latency)大約為4ms,因此一次IO操作的耗時大約為10ms左右。這個數字意味著什么?下文還會提到。

Posix同樣定義了fdatasync,放寬了同步的語義以提高性能:

int fdatasync(int fd);
fdatasync的功能與fsync類似,但是僅僅在必要的情況下才會同步metadata,因此可以減少一次IO寫操作。那么,什么是“必要的情況”呢?根據man page中的解釋:

"fdatasync does not flush modified metadata unless that metadata is needed in order to allow a subsequent data retrieval to be corretly handled."

舉例來說,文件的尺寸(st_size)如果變化,是需要立即同步的,否則OS一旦崩潰,即使文件的數據部分已同步,由于metadata沒有同步,依然讀不到修改的內容。而最后訪問時間(atime)/修改時間(mtime)是不需要每次都同步的,只要應用程序對這兩個時間戳沒有苛刻的要求,基本無傷大雅。

PS:open時的參數O_SYNC/O_DSYNC有著和fsync/fdatasync類似的語義:使每次write都會阻塞等待硬盤IO完成。(實際上,Linux對O_SYNC/O_DSYNC做了相同處理,沒有滿足Posix的要求,而是都實現了fdatasync的語義)相對于fsync/fdatasync,這樣的設置不夠靈活,應該很少使用。

3. 使用fdatasync優化日志同步

文章開頭時已提到,為了滿足事務要求,數據庫的日志文件是常常需要同步IO的。由于需要同步等待硬盤IO完成,所以事務的提交操作常常十分耗時,成為性能的瓶頸。

在Berkeley DB下,如果開啟了AUTO_COMMIT(所有獨立的寫操作自動具有事務語義)并使用默認的同步級別(日志完全同步到硬盤才返回),寫一條記錄的耗時大約為5~10ms級別,基本和一次IO操作(10ms)的耗時相同。

我們已經知道,在同步上fsync是低效的。但是如果需要使用fdatasync減少對metadata的更新,則需要確保文件的尺寸在write前后沒有發生變化。日志文件天生是追加型(append-only)的,總是在不斷增大,似乎很難利用好fdatasync。

且看Berkeley DB是怎樣處理日志文件的:

??????? 1.每個log文件固定為10MB大小,從1開始編號,名稱格式為“log.%010d"

??????? 2.每次log文件創建時,先寫文件的最后1個page,將log文件擴展為10MB大小

??????? 3.向log文件中追加記錄時,由于文件的尺寸不發生變化,使用fdatasync可以大大優化寫log的效率

??????? 4.如果一個log文件寫滿了,則新建一個log文件,也只有一次同步metadata的開銷

4. fflush

??? 標準IO函數(如fread,fwrite等)會在內存中建立緩沖,該函數刷新內存緩沖,將內容寫入內核緩沖,而要想將其真正寫入磁盤,還需要調用fsync。(即先調用fflush然后再調用fsync,否則不會起作用)。fflush以指定的文件流描述符為參數(對應以fopen等函數打開的文件流),僅僅是把上層緩沖區中的數據刷新到內核緩沖區就返回,因此相對于fsync而言不是很安全,還需要再調用一下fsync來把數據真正寫入硬盤。為了實現以上功能,需要把文件流描述符(fp)轉換為文件描述符(fd),以方便fsync的調用,使用以下函數:int fileno(FILE *fp); <- in stdio.


fflush是libc.a中提供的方法,

fsync是系統提供的系統調用。

2.原形

fflush接受一個參數FILE *.

fflush(FILE *);

fsync接受的時一個Int型的文件描述符。

fsync(int fd);

3.功能

fflush:是把C庫中的緩沖調用write函數寫到磁盤[其實是寫到內核的緩沖區]。

fsync:是把內核緩沖刷到磁盤上。

c庫緩沖-----fflush---------〉內核緩沖--------fsync-----〉磁盤


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383402.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383402.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383402.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

二叉樹的廣度優先遍歷(層序遍歷)

先定義一個二叉樹的結點 再創建二叉樹&#xff0c;這里就不寫了&#xff0c;之前的有創建二叉樹的博客。 層序遍歷 用到棧的思想&#xff0c; 1 先讓根 節點進隊列&#xff0c;2 然后讀隊頂元素&#xff0c;3 讓他出隊列4 打印它的值5 讓隊頂元素的左右子樹進棧&#xff0…

用前序中序創建二叉樹(用中序后序創建二叉樹)

定義二叉樹結點 比如就拿這個二叉樹 前序中序創建 因為前序遍歷的順序是 根 &#xff0c; 左 &#xff0c;右。 中序的遍歷是 左 根 右。 我們會很不好想&#xff0c;但我們可以用前序和中序把上面那個二叉樹的遍歷一邊 前序遍歷&#xff1a;ABDEHCFG中序遍歷&#xff1a;D…

Epoll詳解及源碼分析

文章來源&#xff1a;http://blog.csdn.net/chen19870707/article/details/42525887 Author&#xff1a;Echo Chen&#xff08;陳斌&#xff09; Email&#xff1a;chenb19870707gmail.com Blog&#xff1a;Blog.csdn.net/chen19870707 Date&#xff1a;Jan.7th, 2015 1…

非遞歸實現二叉樹(前序,中序,后序)c/c++實現

這里還是用到棧的思想&#xff0c;為了方便用了c的一些內容&#xff0c;把出棧&#xff0c;進棧&#xff0c;讀棧頂元素用一個個函數封裝起來了&#xff0c;前面做了一些處理來使用這些函數。 前序非遞歸 思想&#xff1a;一直走左邊&#xff0c;依次進棧。等左邊為空的時候&…

Linux 中統計一個進程的線程數

如果你想看到 Linux 中每個進程的線程數&#xff0c;有以下幾種方法可以做到這一點。 方法一: /proc proc 偽文件系統&#xff0c;它駐留在 /proc 目錄&#xff0c;這是最簡單的方法來查看任何活動進程的線程數。 /proc 目錄以可讀文本文件形式輸出&#xff0c;提供現有進程和系…

Linux_linux基礎命令(增刪查,權限,Linux下的重要目錄,重要命令(. du, df, top, free, pstack, su, sudo).安裝gcc/g++, gdb, vim )

r&#xff1a;表示可讀w&#xff1a;表示可寫x&#xff1a;表示可執行也可以用數字表示這一點我們會在修改文件權限說明。對于文件夾的rwx表示&#xff1a;r表示可讀及可以查看文件夾內容可以ls查看w表示可寫及可以向文件夾中傳送內容如文件x表示可執行及可以向文件夾中可以cd進…

pthread_create會導致內存泄露

這幾天一直在調試一個系統&#xff0c;系統的功能就是定時發送數據、接收數據然后解析收到的數據&#xff0c;轉換成一定的格式存入數據庫中。我為了并發操作&#xff0c;所以每接收到一個數據包&#xff0c;就調用pthread_create函數創建一個默認屬性的線程進行處理。 系統…

Linux_linux常用工具之make/makefile詳解

make/makefile make/makefile: 項目自動化構建工具 makefile:普通文本文件&#xff0c;記錄了項目的構建流程規則。 make: 一個解釋程序&#xff0c;到當前執行make命令的目錄下尋找makefile文件&#xff0c;并且對makefile 中記錄的項目構建規則進行解釋執行。makefile: 編寫…

Linux_linux常用工具(git,vim ,gcc ,gdb,權限)超詳解

git :項目版本控制工具 項目克隆&#xff1a;git clone項目提交&#xff1a;git add&#xff08;本地倉庫提交&#xff09; git commit -m “bak msg”&#xff08;-m 備注信息&#xff09;同步到服務器&#xff1a;git push origin master&#xff08;提交到主分支&…

T20調試札記

最近在調試T20的內存&#xff0c;使用的指令在此記錄一下 1. pmap指令查看指定進程中的內存分布。該指令需要在busybox中開啟 pmap -x 111 2.應用與so需要執行strip操作&#xff0c;可以減小存儲空間的大小 mips-linux-gnu-strip libsysutils.so 3.nm指令和file指令可以查…

samba 2.2.7a 編譯

今天在君正T20上編譯samba 2.2.7a 遇到了一些問題&#xff0c;特此記錄一下 1.自己寫一個build.sh腳本&#xff0c;方便后續的再次編譯 #!/bin/sh # export CFLAGS"-O2 -muclibc" export CPPFLAGS"-O2 -muclibc" export CXXFLAGS"-O2 -muclibc&qu…

Linux_linux常用工具------進度條程序

緩沖區對文件讀寫的影響&#xff1a;數據并沒有直接寫入文件&#xff0c;而是寫入到緩沖區&#xff08;內存&#xff09;中&#xff0c;等到緩沖區中數據寫滿或者刷新緩沖區的時候&#xff0c;才會將數據真正的寫入文件 fflush&#xff08;stdout&#xff09;刷新。 回車與換行…

Ubuntu下QT的安裝詳細教程

本文轉自&#xff1a;http://blog.chinaunix.net/uid-7945126-id-4987195.html 經測試完美解決 ------------------------------------------------------------- 最近需要在Ubuntu下開發桌面軟件&#xff0c;想起了QT。書上介紹的方法太老了&#xff0c;網上找了一大堆安裝方法…

Linux_linux常用工具---閑雜篇(除了vim, 還有哪些常用的牛逼的編輯器, 并能夠橫向對比編輯器之間的區別和優缺點.)

vim自行查找資料, 自行配置插件. 借鑒別人的 " 顯示相關 “”""""""""""""""""""""""""""""""""&…

ubuntu14.04下安裝qt4.8.6 +qt creator

原創作品&#xff0c;允許轉載&#xff0c;轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://248341.blog.51cto.com/238341/1438867以前安裝時沒太注意&#xff0c;安裝qt后發現在qt creator下無法輸入中文&#xff0c;或者中文無法…

網絡基礎一(協議的概念,網絡應用程序設計模式)

協議的概念 什么是協議&#xff1f; 從應用的角度出發&#xff0c;協議可理解為“規則”&#xff0c;是數據傳輸和數據的解釋的規則。 假設&#xff0c;A、B雙方欲傳輸文件。規定&#xff1a; 第一次&#xff0c;傳輸文件名&#xff0c;接收方接收到文件名&#xff0c;應答OK…

ubuntu修改root密碼

sudo passwd root [sudo] password for you &#xff1a;---> 輸入你的密碼&#xff0c;不會顯示 Enter new UNIX password: --- > 設置root 密碼 Retype new UNIX password: --> 重復

linux 消息隊列機制

現在我們來討論第三種也是最后一種System V IPV工具&#xff1a;消息隊列。在許多方面看來&#xff0c;消息隊列類似于有名管道&#xff0c;但是卻沒有與打開與關閉管道的復雜關聯。然而&#xff0c;使用消息隊列并沒有解決我們使用有名管道所遇到的問題&#xff0c;例如管道上…

堆(概念,數據結構中堆與內存堆區的區別 ,堆的基本操作)

堆的特性&#xff1a; 必須是完全二叉樹 用數組實現 任一結點的值是其子樹所有結點的最大值或最小值 最大值時&#xff0c;稱為“最大堆”&#xff0c;也稱大根堆&#xff1b; 在完全二叉樹中&#xff0c;任何一個子樹的最大值都在這個子樹的根結點。最小值時&#xff0c;稱為…

makefile中的shell調用---注意事項

在之前一次編寫makfile時候&#xff0c;有看到相關的makefile中使用$$來引用變量&#xff0c;而且嘗試后發現$$使用居然和${}有類似的功能。當時也沒具體追究相關的用法&#xff0c;當然剛才所說的都是錯誤的觀念 $$&#xff1a;在makefile中會被替換成一個$。 相關資料是這么描…