mmap 和 read 系統流程
在linux檔案系統中,通常使用open(), read()讀取檔案,但作業系統同樣提供了mmap()作為讀取檔案的方式,而這兩者有什麼不同呢?什麼時候用read(), 什麼時候用mmap()?
首先,read 的通常使用方法是 read(fd, buffer, size)
,將要讀取的資料讀到buffer中。這就涉及到兩個步驟,read是系統呼叫函式,每次使用read都要進入核心態,進行上下文切換。核心首先將檔案資料從磁碟讀入page cache快取,再將資料從page cache拷貝到buffer中。上下文切換和拷貝要消耗一定效能。
而如果使用 mmap 命令,VFS(虛擬檔案系統)會分配對應的虛擬記憶體空間,記錄目標檔案的 inode 和其他屬性,將起始虛擬地址返回給程式。當程式想要訪問某部分資料時,需要進行地址翻譯,但此時沒有更新頁表,會觸發缺頁中斷。linux根據VMA中記錄的 inode 資訊,呼叫對應的檔案系統進行處理。檔案系統讀取該頁,返回給VFS,VFS再更新頁表,返回對應的物理頁。
在 mmap 之後,後續的讀寫操作都是在記憶體中進行,不需要再讀磁碟和進入核心態。
mmap的優點
因此 mmap 比起 read ,有如下優勢:
- 對於隨機訪問,不用頻繁 lseek。因為 mmap 是將整個檔案對映到虛擬空間,在讀取時再按需分配實體記憶體。
- 減少後續系統呼叫次數。後續讀檔案時不需要再進入核心態,減少了上下文切換
- 減少資料拷貝。免去了page cache 到 buffer 的資料拷貝。
- 當多個程式將同一頁面對映到記憶體時,資料可以在這些程式之間共享。對於 只讀 的頁面可以完全共享,需要寫入的檔案可以使用COW(copy on write)私有化。這樣節省了大量記憶體。
mmap
also allows the operating system to optimize paging operations. For example, consider two programs; programA
which reads in a1MB
file into a buffer creating withmalloc
, and program B whichmmaps
the 1MB file into memory. If the operating system has to swap part ofA
's memory out, it must write the contents of the buffer to swap before it can reuse the memory. InB
's case any unmodifiedmmap
'd pages can be reused immediately because the OS knows how to restore them from the existing file they weremmap
'd from. (The OS can detect which pages are unmodified by initially marking writablemmap
'd pages as read only and catching seg faults, similar to Copy on Write strategy).
mmap 還可以優化作業系統分頁。對於程式A、B,如果A通過 read 讀取了1MB資料到buffer中,而B通過 mmap 讀取1MB資料。如果OS想要把A中的 buffer 換入磁碟,首先要將buffer中的內容寫入磁碟,才可以重用該物理頁。而對於B中沒有被修改過的 mmap 頁,OS可以直接重用,因為OS可以從檔案中再重新讀取該頁來恢復資料。
那麼,如果 mmap 比起 open(),read() 有這麼多優點,為什麼不用 mmap 呢?對於系統來說,有優點往往意味著存在對應的缺點,這才是系統設計中的trade off。
mmap的缺點
- mmap 每次以頁為單位從檔案中讀取資料,因此對映的頁面大小始終是整數。對於小檔案可能會造成較多的內部碎片。同時,在讀取資料時也需要顯式修正資料在頁面中的偏移量。
- mmap 需要連續的虛擬記憶體空間用於儲存檔案,如果檔案較大,對於32位地址空間的系統來說,可能找不到足夠大的連續區域。
- mmap 本身開銷比 read 大,因為mmap涉及更多的系統呼叫,需要觸發缺頁中斷,更改虛擬記憶體對映。
總結
由於read 讀取檔案更加直觀和易於理解,因此初學者依然使用 read 較多。但如果需要隨機訪問資料,或者和其他程式共享資料,用 mmap 不失為一個更好的選擇。