虚拟内存分页机制的页面置换
前言
之前簡單介紹過虛擬內存是如何與物理內存進行地址映射的: 虛擬內存分頁機制的地址映射, 但是僅僅地址映射是不夠的, 在地址映射說過會有缺頁的情況, 此時就需要操作操作系統將缺少的頁加載到內存中. 但是, 如果內存滿了怎么辦呢? 畢竟虛擬內存一般都要大于物理內存的, 不可能將所有虛擬內存中的內容都加載到物理內存中.
當需要加載虛擬內存中的內容時, 發現物理內存已經沒有空閑空間了. 腫么辦嘞? 淘汰一個舊頁面, 就可以騰出空間來加載新的頁面了. 既然涉及到淘汰, 那么淘汰哪一個頁面就是一個問題了. 這篇文章就簡單介紹一下幾個頁面置換的算法, 或者說是頁面淘汰的算法.
頁面置換算法
什么樣的頁面置換算法是好的呢? 簡單說, 就是盡可能的減少缺頁中斷的次數. 在發生缺頁中斷的時候, 是要損失一部分性能的.
最優頁面置換
如果有4個頁面已經加載了, 現在要在它們之間選擇一個進行淘汰, 選擇哪一個呢? 要想讓中斷發生的次數最少, 那么馬上就要用到的頁面是不能被淘汰的, 這么算下來, 就應該淘汰那個在訪問序列中最后的頁面. 舉個例子:
為什么要在最開始先將內存填滿, 而不是用到的時候再進行加載呢? 個人認為, 有如下好處
因為例子的局限性, 例子僅用作對置換規則的理解, 不用作各個算法優劣的比較
但是, 別高興的太早, 這種情況太過于理想化了, 操作系統是無法預知未來的, 因此這個算法并不能在實際中應用. 講話了, 不能用你提他干啥啊, 雖然不能實際實現, 但可以作為一個評價其他算法的優劣的標準呀, 越接近最優頁面置換的就越好.
接下來, 幾個實際應用的置換算法將相繼登場(因為無法預測未來, 他們都是基于歷史訪問來判斷的):
先進先出
既FIFO, 看名字就能看出來, 那個頁面先進來, 哪個頁面就先淘汰. 還使用相同的序列進行舉例:
但是, 注意看我標記tip1的地方, 頁面b在上一次訪問剛剛被淘汰, 馬上就又要用到了, 這這這…
實現
先進先出的方式, 實現起來很簡單, 只需要維護一個頁面換入的隊列即可. 淘汰時從隊首取出淘汰, 放入是添加到隊尾即可.
Belady 現象
提到了先進先出, 那么就要說一下這個算法反嘗試的Belady現象. 不用去找belady這個單詞的意思了, 他是首次發現這種顯得的前輩名字.
在常識中, 隨著物理頁容量增大, 那么缺頁中斷的頻率也會下降, 因為可以將更多的頁面放入內存中了嘛. 但是, FIFO可能會出現這樣一種情況: 隨著物理頁容量增大, 缺頁中斷的頻率也隨之增大.
舉個例子: 有這樣一個訪問序列: [a, b, c, d, a, b, e, a, b, c, d, e]. 此時物理也中數據為空, 依次將頁面讀入內存. 過程就不說了, 直接說結果:
- 當物理頁數量為3時, 產生9次缺頁中斷
- 當物理頁數量為4時, 產生10次缺頁中斷
而這種Belady現象, 只有FIFO有, 其他算法都沒有, 我特意造了寫訪問序列, 想找到其他算法也存在這種現象的情況, 很可惜, 沒有找到. 不過已經就這種現象寫了論文描述, 有時間嘗試這看看.
最近最久未使用(LRU)
這就是大名鼎鼎的LRU了. 需要淘汰的時候, 剛剛被訪問過的頁面不能被淘汰, 那就淘汰那個已經很久沒有被訪問過的頁面. 再次舉例:
有沒有發現, 其實LRU是在仿最優算法, 思路就是, 雖然我無法預測未來, 但是過去我是知道的, 又根據程序的局部性原理, 如果一個頁剛剛被訪問過, 那么他很大概率馬上會再次訪問. 而已經很久沒有被訪問的頁面, 未來可能也不會訪問.
雖然過去不能完全預測未來, 但好在程序的局部性原理又救了我們, 可以說是最優算法的一個近似解了. 但是別高興的太早, 思路再好也要拿出可行的方案才行.
實現
要找到哪一個頁面已經很久沒有訪問過了, 可以維護一個訪問序列的鏈表, 首部是剛剛被訪問的頁面, 尾部就是很久都沒有被訪問的頁面. 這樣淘汰頁面的時候直接取尾部的頁面進行淘汰.
要維護這樣的一個鏈表, 就需要在每次訪問內存, 從鏈表中找到這個頁面, 將其移動到首部
為了維護這樣一個鏈表, 給每次內存訪問都增加了額外的負擔, 操作系統占用的時間越久, 留給應用程序的時間響應的就越少. 可以說得不償失, 開銷太大了.
所以, 雖然LRU看上去很美好, 但是沒有一個高效的算法來實現它
時鐘頁面置換(二次機會)
在之前介紹頁面的地址映射時說過, 頁表中的每一頁都存在著一些標志位, 而其中的一個標志位, 標識當前頁是否被訪問過, 當頁面被訪問時, 會由硬件負責將此標記為置為1, 注意是由硬件來完成的, 所以效率是很高的.
我們能不能利用這個標志位來實現頁面置換呢? 參考LRU算法, 淘汰那個已經很久沒有訪問過的頁面. 如果說, 先將所有訪問位置為0, 等到下一次需要淘汰頁面的時候, 找到一個訪問位仍是0的頁面, 說明在這期間這個頁面從來沒有被訪問過不就可以了么
但是, 現實中的內存動輒幾個 G, 如果每次都將所有的頁面標識位修改一遍, 效率也是很慢的. 為了提高效率, 可以對這個算法再次進行近似. 將所有的頁面連接為一個環, 使用一個指針在環上遍歷, 每次需要淘汰頁面的時候, 指針就開始遍歷, 找到第一個訪問位為0的頁面淘汰, 同時在遍歷的期間, 順便把經過的頁面訪問位置為0. 這樣每次只掃描部分頁面, 最差情況掃描所有(將當前置為0, 掃描一圈回來必定拿到0), 故此算法的步驟如下:
- 若指針當前指向的頁面, 訪問位為0, 直接淘汰并指向下一頁. 否則進入下一步
- 若當前頁訪問位為1, 將其改為0并指向下一頁, 回到上一步.
對于被訪問過的頁面, 會在第二次掃描的時候進行淘汰, 算是給了兩次機會吧.
還用剛才的序列進行舉例:
時鐘算法可以說是對LRU的一個可實現的近似解了. 據說在實際應用中, 效率是比較接近LRU的.
增強版時鐘算法
在選擇頁面淘汰的時候, 如果兩個頁面都不會被訪問了, 淘汰一個和淘汰另一個有區別么? 有的, 別忘了, 頁面置換的時候, 不光換入, 還有換出的操作, 也就還是會將淘汰頁的數據寫回磁盤, 當然如果頁面沒有被修改就不需要寫回的操作了, 數據都一樣嘛. 怎么知道頁面有沒有被修改呢? 巧了, 也有一個標記頁面修改的標記位.
剛剛的時鐘算法用到了標志位中的訪問位, 那么如何將修改位也加入到判斷標準中, 優先淘汰沒有被修改的頁面, 就能夠提高頁面置換的效率了. 兩個標志位的話, 就有如下四種情況, 分情況討論:
- (訪問位1, 修改位0): 最近訪問過, 將訪問位置為0
- (訪問位1, 修改位1): 最近訪問過, 將訪問位置為0
- (訪問位0, 修改位0): 最近沒有訪問且沒有修改, 可以直接置換
- (訪問位0, 修改位1): 最近沒有訪問但修改過, 將修改位置為0, 等待下一輪訪問
但是, 但是, 你有沒有想過, 如果將標志位中的修改位置為0了, 那么操作系統進行頁面置換的時候, 依據什么來判斷當前頁是否需要執行回寫磁盤的操作呢? 所以, 修改位是不能動的. 如果不動修改位, 又如何來實現呢? 那就要增加額外變量來記錄了:
- 第一圈掃描的時候, 淘汰(訪問位0, 修改位0)的頁面
- 同時在掃描的過程中將訪問位改為0
- 如果第一圈沒找到, 那么第二圈淘汰(訪問位0, 修改位1)的頁面
也就是說, 和時鐘算法對數據的處理規則相同, 唯一不同的是額外增加臨時變量記錄當前是第幾圈, 第一圈淘汰(訪問位0, 修改位0)的頁面, 第二圈才會淘汰(訪問位0, 修改位1)的頁面. 也就是將修改過的頁面進行降權操作.(當然, 也可以有其他實現方式, 不過大體意思不變) 再次舉例:
如此操作, 使得被修改的頁面更不容易被換出去, 進而提升頁面置換效率. 實現起來和時鐘算法相似. 是時鐘算法的增強版.
最不常用(LFU)
既LFU, 在淘汰的時候, 淘汰使用次數最少的頁面. 也是一種依據過去預測未來的思路, 過去使用較多的頁, 很可能在未來也會多次訪問. LRU的考察緯度是時間, 而LFU的考察緯度是次數. 再次上圖:
有這樣一種情況, 程序在初始的時候頻繁訪問頁面 A, 等到程序平穩運行了, 就不會再訪問頁面 A 了. 但是, 因為計數的結果, 頁面 A 的訪問次數及高, 導致一直沒有被淘汰, 長期駐留在內存中. 如何避免這樣的問題呢? 其實也很簡單, 出現問題是因為增加了時間緯度, 只需要每個一段時間將頁面的計數左移一位(除以2), 這種頁面的訪問次數就會隨著時間推移降下來了.
實現
既然淘汰的依據是頁面的訪問次數, 那么我們就要知道哪個頁面訪問次數多, 哪個頁面訪問次數少. 最直觀的思路是, 記錄每一頁的訪問次數, 淘汰時找到值最小的就行了. 但是, 有一個與LRU相同的問題, 你如何來實現這個算法? 在每一次訪問頁面的時候執行計數器加一的操作么? 代價太大.
全局頁面置換
前面介紹的幾個頁面置換算法, 都假設物理內存容量固定且操作系統中只運行一個進程. 但這和實中是有區別的, 操作系統中運行著很多進程, 每個進程被分配到的物理內存容量都是不同的. 亦或者一個進程剛開始運行的時候會在多個頁面之間切換訪問, 而隨著運行平穩之后訪問的頁面集中在其中的幾個.
這里考慮的是, 如何來確定給不同進程分配的物理內存大小以使得總體的缺頁中斷率較低. 甚至在進程的不同階段分配不同大小的物理內存.
全局頁面置換算法又有好多, 這里簡單提幾個, 就不展開說了
工作集頁面置換
工作集就是進程在最近 t個時刻所訪問過的頁面. 其運行規則如下:
- 頁面淘汰時, 有限淘汰不再工作集中的
- 若都在工作集中, 可通過上述的頁面置換處理
- 若頁面已經不在工作集中, 會進行釋放
- 這里與前面的算法產生差異了, 即使沒有發生缺頁中斷, 也會進行頁面的釋放
- 從而可以將空閑內存交給其他進程使用
缺頁率頁面置換
基于工作集頁面置換的思想, 當缺頁率變大時增加工作集大小, 以使得更多的頁面放到內存中. 當缺頁率變小時減小工作集大小, 以使得頁面得到釋放, 提高內存整體利用率.
不過需要計算缺頁率的原因, 會導致額外開銷的增加.
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的虚拟内存分页机制的页面置换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: div独占一行 html_web前端基础
- 下一篇: 揭开HTTPS的神秘面纱