參考文獻:

從核心檔案系統看檔案讀寫過程》http://www.cnblogs.com/huxiao-tee/p/4660352.html?utm_source=tuicool&utm_medium=referral

《mmap是什麼》http://www.cnblogs.com/huxiao-tee/p/4660352.html?utm_source=tuicool&utm_medium=referral

正文:

首先說一下檔案系統,Linux檔案系統的三層結構想必大家多少都瞭解一些,每個程序中都有一個使用者檔案描述符表,表項指向一個全域性的檔案表中的某個表項,檔案表表項有一個指向記憶體inode的指標,每個inode唯一標識一個檔案。如果同時有多個程序開啟同一檔案,他們的使用者檔案描述符表項指向不同的檔案表項,但是這些檔案表項會指向同一個inode。

此時又會引出另外一個東東:page cache。核心會為每個檔案單獨維護一個page cache,使用者程序對於檔案的大多數讀寫操作會直接作用到page cache上,核心會選擇在適當的時候將page cache中的內容寫到磁碟上(當然我們可以手工fsync控制回寫),這樣可以大大減少磁碟的訪問次數,從而提高效能。Page cache是linux核心檔案訪問過程中很重要的資料結構,page cache中會儲存使用者程序訪問過得該檔案的內容,這些內容以頁為單位儲存在記憶體中,當用戶需要訪問檔案中的某個偏移量上的資料時,核心會以偏移量為索引,找到相應的記憶體頁,如果該頁沒有讀入記憶體,則需要訪問磁碟讀取資料。為了提高頁得查詢速度同時節省page cache資料結構佔用的記憶體,linux核心使用樹來儲存page cache中的頁。

在瞭解了以上的基礎之後,我們就來比較一下mmap和read/write的區別,先說一下read/write系統呼叫,read/write系統呼叫會有以下的操作:

  1. 訪問檔案,這涉及到使用者態到核心態的轉換
  2. 讀取硬碟檔案中的對應資料,核心會採用預讀的方式,比如我們需要訪問100位元組,核心實際會將按照4KB(記憶體頁的大小)儲存在page cache中
  3. 將read中需要的資料,從page cache中拷貝到使用者緩衝區中

整個過程還是比較艱辛的,基本上涉及到使用者核心態的切換,還有就是資料拷貝接下來繼續說mmap吧,mmap系統呼叫是將硬碟檔案對映到用記憶體中,說的底層一些是將page cache中的頁直接對映到使用者程序地址空間中,從而程序可以直接訪問自身地址空間的虛擬地址來訪問page cache中的頁,這樣會並涉及page cache到使用者緩衝區之間的拷貝,mmap系統呼叫與read/write呼叫的區別在於:

  1. mmap只需要一次系統呼叫,後續操作不需要系統呼叫
  2. 訪問的資料不需要在page cache和使用者緩衝區之間拷貝

從上所述,當頻繁對一個檔案進行讀取操作時,mmap會比read高效一些。

最後再說一下page cache的話題,從上面所說我們從磁碟檔案中讀取的內容都會存在page cache中,但當我們關閉這個檔案時,page cache中內容會立馬釋放掉嗎?答案是否,磁碟的讀取速度比記憶體慢太多,如果能命中page cache可以顯著提升效能,萬一後續又有對這個檔案的操作,系統就可以很快速的響應。當然,這些檔案內容也不是一直存在page cache中的,一般只要系統有空閒實體記憶體,核心都會拿來當快取使用,但當實體記憶體不夠用,記憶體會清理出部分page cache應急,這也就是告訴我們程式對於實體記憶體的使用能省則省,交給核心使用,作用很大。

還有就是普通的write呼叫只是將資料寫到page cache中,並將其標記為dirty就返回了,磁碟I/O通常不會立即執行,這樣做的好處是減少磁碟的回寫次數,提供吞吐率,不足就是機器一旦意外掛掉,page cache中的資料就會丟失。一般安全性比較高的程式會在每次write之後,呼叫fsync立即將page cache中的內容回寫到磁碟中。