參考文獻:
《從內核文件系統看文件讀寫過程》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系統調用會有以下的操作:
- 訪問文件,這涉及到用戶態到內核態的轉換
- 讀取硬盤文件中的對應數據,內核會采用預讀的方式,比如我們需要訪問100字節,內核實際會將按照4KB(內存頁的大小)存儲在page cache中
- 將read中需要的數據,從page cache中拷貝到用戶緩沖區中
整個過程還是比較艱辛的,基本上涉及到用戶內核態的切換,還有就是數據拷貝接下來繼續說mmap吧,mmap系統調用是將硬盤文件映射到用內存中,說的底層一些是將page cache中的頁直接映射到用戶進程地址空間中,從而進程可以直接訪問自身地址空間的虛擬地址來訪問page cache中的頁,這樣會並涉及page cache到用戶緩沖區之間的拷貝,mmap系統調用與read/write調用的區別在於:
- mmap只需要一次系統調用,后續操作不需要系統調用
- 訪問的數據不需要在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中的內容回寫到磁盤中。