读取文件慢_页面缓存(Page Cache)-内存和文件之间的那点事儿(下)
翻譯:RobotCode俱樂部
現在來做一個測驗。假設render程序的最后一個進程實例已經退出,會立即釋放頁面緩存中的scene.dat數據?人們經常這么認為的,但這不是一個好主意。你有沒有想到,我們通常在一個程序中創建一個文件,然后退出,然后在另一個程序中要使用該文件。頁面緩存也必須處理這種情況。所以內核為什么要刪除頁面緩存內容呢?請記住,磁盤比RAM慢5個數量級,因此頁面緩存命中是一個巨大的勝利。只要有足夠的空閑物理內存,緩存就應該被盡可能的使用。因此,它不依賴于特定的進程,而是一個系統范圍的資源。如果你運行render進程一周后,發現scene.dat仍然被緩存,這太幸運了!這就是為什么內核緩存的大小會穩步上升,直到達到上限。這并不是因為操作系統是垃圾并占用你的RAM,這實際上是一種良好的行為,因為從某種程度上說,釋放物理內存才是一種浪費。
由于頁面緩存的存在,當程序調用write()寫字節時,只需將其復制到頁面緩存中,并將頁面標記為dirty。磁盤I/O通常不會立即發生,因此你的程序不會阻塞等待磁盤。不利的一面是,如果計算機崩潰,您的寫操作將永遠無法完成,因此像數據庫事務日志這樣的關鍵文件必須調用fsync()立刻寫入到磁盤(但是仍然需要擔心驅動器控制器的緩存,也可能造成并不會立刻寫入到物理磁盤)。另一方面,讀取通常會阻塞程序,直到數據可用為止。內核使用預先加載技術來緩解這個問題,其中一個例子是提前讀取,內核將幾個頁面預加載到頁面緩存中,等待你的讀取。你可以通過調整內核的一些選項來調整預先加載行為,可以控制順序讀取文件還是隨機讀取文件。Linux確實對內存映射文件進行預讀,但我不確定Windows是否如此。最后,可以在Linux中使用O_DIRECT繞過頁面緩存,或者在Windows中使用NO_BUFFERING繞過頁面緩存,這是數據庫軟件經常做的事情。
文件映射可以是私有的或共享的。這是針對內存中的內容所做的更新:在私有映射中,更新不提交到磁盤或使其他進程可見,而在共享映射中,更新是可見的。內核使用頁表項啟用的寫時復制機制來實現私有映射。在下面的例子中,render和render3d都私有映射了文件scene.dat。render然后寫入它的虛擬內存區域中的映射文件:
上面所示的只讀頁表項并不意味著映射是只讀的,它們只是在最后一刻共享物理內存的內核實現技巧。你可以看到“private”是多么的不恰當,只要記住它只適用于更新,讀取不是私有的,也是共享的。這種設計的結果是,通過映射文件的虛擬頁面可以反映出有其他程序對該文件做了更改。一旦寫時復制完成,其他人的更改將不再可見。內核不能保證這種行為,但它是在x86中得到的,(是通過硬件方式實現的?這里不太明白)從API的角度來看是有意義的。相比之下,共享映射僅僅映射到頁面緩存,僅此而已。更新對于其他進程是可見的,并最終保存在磁盤中。最后,如果上面的映射是只讀的,那么頁面錯誤將觸發段錯誤,而不是寫入時的復制錯誤。
動態加載的庫通過文件映射到程序的地址空間。這沒有什么神奇的,它是同樣的私有文件映射。下面的示例展示了兩個運行實例部分地址空間中的文件映射,以及物理內存使用情況,以便我們將上述許多概念聯系在一起。
關于內存基礎的3部分系列文章到此結束。我希望這個系列是有用的,并為你提供了Linux內核內存管理的宏觀機制。
--END
這3部分匯總如下:
RobotCode俱樂部:程序的內存布局(上)
RobotCode俱樂部:程序的內存布局(下)
RobotCode俱樂部:內核是如何管理內存的?(上)
RobotCode俱樂部:內核是如何管理內存的?(中)
RobotCode俱樂部:內核是如何管理內存的?(下)
RobotCode俱樂部:頁面緩存(Page Cache)-內存和文件之間的那點事兒(上)
RobotCode俱樂部:頁面緩存(Page Cache)-內存和文件之間的那點事兒(下)
由于本人水平有限,翻譯必然有很多不妥的地方,歡迎指正。同時,歡迎關注下方微信公眾號,一起交流學習:)
總結
以上是生活随笔為你收集整理的读取文件慢_页面缓存(Page Cache)-内存和文件之间的那点事儿(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 6开头的是什么股票
- 下一篇: 基金套牢是什么意思啊