成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

頁面緩存、內存和文件之間的那些事

系統 Linux Windows
上一篇文章中我們學習了內核怎么為一個用戶進程 管理虛擬內存,而沒有提及文件和 I/O。這一篇文章我們將專門去講這個重要的主題 —— 頁面緩存。文件和內存之間的關系常常很不好去理解,而它們對系統性能的影響卻是非常大的。

[[225278]]

上一篇文章中我們學習了內核怎么為一個用戶進程 管理虛擬內存,而沒有提及文件和 I/O。這一篇文章我們將專門去講這個重要的主題 —— 頁面緩存。文件和內存之間的關系常常很不好去理解,而它們對系統性能的影響卻是非常大的。

在面對文件時,有兩個很重要的問題需要操作系統去解決。***個是相對內存而言,慢的讓人發狂的硬盤驅動器,尤其是磁盤尋道。第二個是需要將文件內容一次性地加載到物理內存中,以便程序間共享文件內容。如果你在 Windows 中使用 進程瀏覽器 去查看它的進程,你將會看到每個進程中加載了大約 ~15MB 的公共 DLL。我的 Windows 機器上現在大約運行著 100 個進程,因此,如果不共享的話,僅這些公共的 DLL 就要使用高達 ~1.5 GB 的物理內存。如果是那樣的話,那就太糟糕了。同樣的,幾乎所有的 Linux 進程都需要 ld.so 和 libc,加上其它的公共庫,它們占用的內存數量也不是一個小數目。

幸運的是,這兩個問題都用一個辦法解決了:頁面緩存 —— 保存在內存中的頁面大小的文件塊。為了用圖去說明頁面緩存,我捏造出一個名為 render 的 Linux 程序,它打開了文件 scene.dat,并且一次讀取 512 字節,并將文件內容存儲到一個分配到堆中的塊上。***次讀取的過程如下:

Reading and the page cache

Reading and the page cache

  1. render 請求 scene.dat 從位移 0 開始的 512 字節。
  2. 內核搜尋頁面緩存中 scene.dat 的 4kb 塊,以滿足該請求。假設該數據沒有緩存。
  3. 內核分配頁面幀,初始化 I/O 請求,將 scend.dat 從位移 0 開始的 4kb 復制到分配的頁面幀。
  4. 內核從頁面緩存復制請求的 512 字節到用戶緩沖區,系統調用 read() 結束。

讀取完 12KB 的文件內容以后,render 程序的堆和相關的頁面幀如下圖所示:

Non-mapped file read

Non-mapped file read

它看起來很簡單,其實這一過程做了很多的事情。首先,雖然這個程序使用了普通的讀取(read)調用,但是,已經有三個 4KB 的頁面幀將文件 scene.dat 的一部分內容保存在了頁面緩存中。雖然有時讓人覺得很驚奇,但是,普通的文件 I/O 就是這樣通過頁面緩存來進行的。在 x86 架構的 Linux 中,內核將文件認為是一系列的 4KB 大小的塊。如果你從文件中讀取單個字節,包含這個字節的整個 4KB 塊將被從磁盤中讀入到頁面緩存中。這是可以理解的,因為磁盤通常是持續吞吐的,并且程序一般也不會從磁盤區域僅僅讀取幾個字節。頁面緩存知道文件中的每個 4KB 塊的位置,在上圖中用 #0#1 等等來描述。Windows 使用 256KB 大小的視圖view,類似于 Linux 的頁面緩存中的頁面page

不幸的是,在一個普通的文件讀取中,內核必須拷貝頁面緩存中的內容到用戶緩沖區中,它不僅花費 CPU 時間和影響 CPU 緩存在復制數據時也浪費物理內存。如前面的圖示,scene.dat 的內存被存儲了兩次,并且,程序中的每個實例都用另外的時間去存儲內容。我們雖然解決了從磁盤中讀取文件緩慢的問題,但是在其它的方面帶來了更痛苦的問題。內存映射文件是解決這種痛苦的一個方法:

Mapped file read

Mapped file read

當你使用文件映射時,內核直接在頁面緩存上映射你的程序的虛擬頁面。這樣可以顯著提升性能:Windows 系統編程 報告指出,在相關的普通文件讀取上運行時性能提升多達 30% ,在 Unix 環境中的高級編程 的報告中,文件映射在 Linux 和 Solaris 也有類似的效果。這取決于你的應用程序類型的不同,通過使用文件映射,可以節約大量的物理內存。

對高性能的追求是永恒不變的目標,測量是很重要的事情,內存映射應該是程序員始終要使用的工具。這個 API 提供了非常好用的實現方式,它允許你在內存中按字節去訪問一個文件,而不需要為了這種好處而犧牲代碼可讀性。在一個類 Unix 的系統中,可以使用 mmap 查看你的 地址空間,在 Windows 中,可以使用 CreateFileMapping,或者在高級編程語言中還有更多的可用封裝。當你映射一個文件內容時,它并不是一次性將全部內容都映射到內存中,而是通過 頁面故障 來按需映射的。在 獲取 需要的文件內容的頁面幀后,頁面故障句柄 映射你的虛擬頁面 到頁面緩存上。如果一開始文件內容沒有緩存,這還將涉及到磁盤 I/O。

現在出現一個突發的狀況,假設我們的 render 程序的***一個實例退出了。在頁面緩存中保存著 scene.dat 內容的頁面要立刻釋放掉嗎?人們通常會如此考慮,但是,那樣做并不是個好主意。你應該想到,我們經常在一個程序中創建一個文件,退出程序,然后,在第二個程序去使用這個文件。頁面緩存正好可以處理這種情況。如果考慮更多的情況,內核為什么要清除頁面緩存的內容?請記住,磁盤讀取的速度要慢于內存 5 個數量級,因此,***一個頁面緩存是一件有非常大收益的事情。因此,只要有足夠大的物理內存,緩存就應該保持全滿。并且,這一原則適用于所有的進程。如果你現在運行 render 一周后, scene.dat 的內容還在緩存中,那么應該恭喜你!這就是什么內核緩存越來越大,直至達到***限制的原因。它并不是因為操作系統設計的太“垃圾”而浪費你的內存,其實這是一個非常好的行為,因為,釋放物理內存才是一種“浪費”。(LCTT 譯注:釋放物理內存會導致頁面緩存被清除,下次運行程序需要的相關數據,需要再次從磁盤上進行讀取,會“浪費” CPU 和 I/O 資源)***的做法是盡可能多的使用緩存。

由于頁面緩存架構的原因,當程序調用 write() 時,字節只是被簡單地拷貝到頁面緩存中,并將這個頁面標記為“臟”頁面。磁盤 I/O 通常并不會立即發生,因此,你的程序并不會被阻塞在等待磁盤寫入上。副作用是,如果這時候發生了電腦死機,你的寫入將不會完成,因此,對于至關重要的文件,像數據庫事務日志,要求必須進行 fsync()(仍然還需要去擔心磁盤控制器的緩存失敗問題),另一方面,讀取將被你的程序阻塞,直到數據可用為止。內核采取預加載的方式來緩解這個矛盾,它一般提前預讀取幾個頁面并將它加載到頁面緩存中,以備你后來的讀取。在你計劃進行一個順序或者隨機讀取時(請查看 madvise()readahead()Windows 緩存提示 ),你可以通過提示hint幫助內核去調整這個預加載行為。Linux 會對內存映射的文件進行 預讀取,但是我不確定 Windows 的行為。當然,在 Linux 中它可能會使用 O_DIRECT 跳過預讀取,或者,在 Windows 中使用 NO_BUFFERING 去跳過預讀,一些數據庫軟件就經常這么做。

一個文件映射可以是私有的,也可以是共享的。當然,這只是針對內存中內容的更新而言:在一個私有的內存映射上,更新并不會提交到磁盤或者被其它進程可見,然而,共享的內存映射,則正好相反,它的任何更新都會提交到磁盤上,并且對其它的進程可見。內核使用寫時復制copy on write(CoW)機制,這是通過頁面表條目page table entry(PTE)來實現這種私有的映射。在下面的例子中,render 和另一個被稱為 render3d 的程序都私有映射到 scene.dat 上。然后 render 去寫入映射的文件的虛擬內存區域:

The Copy-On-Write mechanism

The Copy-On-Write mechanism

  1. 兩個程序私有地映射 scene.dat,內核誤導它們并將它們映射到頁面緩存,但是使該頁面表條目只讀。
  2. render 試圖寫入到映射 scene.dat 的虛擬頁面,處理器發生頁面故障。
  3. 內核分配頁面幀,復制 scene.dat 的第二塊內容到其中,并映射故障的頁面到新的頁面幀。
  4. 繼續執行。程序就當做什么都沒發生。

上面展示的只讀頁面表條目并不意味著映射是只讀的,它只是內核的一個用于共享物理內存的技巧,直到盡可能的***一刻之前。你可以認為“私有”一詞用的有點不太恰當,你只需要記住,這個“私有”僅用于更新的情況。這種設計的重要性在于,要想看到被映射的文件的變化,其它程序只能讀取它的虛擬頁面。一旦“寫時復制”發生,從其它地方是看不到這種變化的。但是,內核并不能保證這種行為,因為它是在 x86 中實現的,從 API 的角度來看,這是有意義的。相比之下,一個共享的映射只是將它簡單地映射到頁面緩存上。更新會被所有的進程看到并被寫入到磁盤上。最終,如果上面的映射是只讀的,頁面故障將觸發一個內存段失敗而不是寫到一個副本。

動態加載庫是通過文件映射融入到你的程序的地址空間中的。這沒有什么可奇怪的,它通過普通的 API 為你提供與私有文件映射相同的效果。下面的示例展示了映射文件的 render 程序的兩個實例運行的地址空間的一部分,以及物理內存,嘗試將我們看到的許多概念綜合到一起。

Mapping virtual memory to physical memory

Mapping virtual memory to physical memory

這是內存架構系列的第三部分的結論。我希望這個系列文章對你有幫助,對理解操作系統的這些主題提供一個很好的思維模型。 

責任編輯:龐桂玉 來源: Linux中國
相關推薦

2019-07-15 15:37:31

頁面緩存內存

2022-07-08 15:09:26

Linux

2021-05-17 08:18:35

Java內存模型JMM

2018-03-01 15:03:11

2019-11-19 14:48:00

Kafka文件存儲

2014-06-06 16:08:17

初志科技

2011-09-19 15:40:35

2020-07-29 08:14:59

云計算云遷移IT

2011-05-19 16:47:50

軟件測試

2012-05-01 08:06:49

手機

2012-05-31 09:53:38

IT風云15年

2017-05-15 21:50:54

Linux引號

2024-02-04 17:03:30

2013-03-12 10:19:20

計算機內存芯片數據

2015-08-20 09:17:36

Java線程池

2015-09-14 09:28:47

2009-02-19 10:21:00

路由多WAN口

2017-03-08 08:53:44

Git命令 GitHub

2021-08-11 21:46:47

MySQL索引join

2021-03-18 16:05:20

SSD存儲故障
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精精品 | 亚洲视频欧美视频 | 欧美亚洲第一区 | 欧美又大粗又爽又黄大片视频 | 美女天堂 | 久久婷婷国产香蕉 | 日本午夜免费福利视频 | 国产欧美精品区一区二区三区 | 亚洲精品久久久久久宅男 | 色综合久久88色综合天天 | 国产成人精品一区 | 国产精品一区二区av | 国产一区二区a | 欧美在线一区二区三区 | 久久精品久久久久久 | 亚洲成av| 国产一区二区免费电影 | 国产精品资源在线观看 | 久久lu | 天天宗合网 | 日韩在线第一 | 国产欧美一区二区三区日本久久久 | 亚洲精品字幕 | 凹凸日日摸日日碰夜夜 | 日韩在线精品强乱中文字幕 | 天天影视网天天综合色在线播放 | 午夜免费福利电影 | 成人在线免费视频 | 国产在线拍偷自揄拍视频 | 久久久久久久久综合 | 一区二区免费视频 | 国产精品美女久久久av超清 | 性色av一区 | 天堂国产 | 99久久99久久精品国产片果冰 | 国产精品视频播放 | 日韩精品一区二区三区视频播放 | 免费在线视频精品 | 五月激情久久 | 国产精品久久久久久一区二区三区 | 亚洲免费视频在线观看 |