記憶體管理-LRU,NFU演算法實現
LRU和NFU的演算法實現
(1)LRU演算法實現
LRU,湯子瀛《計算機作業系統》譯為“最近最久未使用”,也即在緩衝的所有頁面中,缺頁中斷髮生時,將最久未被使用的頁面置換出去。不過按照字面意思,Least Recently Used似乎應是《現代作業系統》中譯版的“最近最少使用”,似乎是需要統計頁面使用頻率的。這裡有必要先探討下這個翻譯問題。這個翻譯的區別在於,副詞least修飾的是recently還是used。P206原文:
A good approximation to the optimal algorithm is based on the observation that pages that have been heavily used in the last few instructions will probably be heavily used again in the next few. Conversely, pages that have not been used for ages will probably remain unused for a long time. This idea suggests a realizable algorithm: when a page fault occurs, throw out the page that has been unused for the longest time. This strategy is called LRU (Least Recently Used) paging.
這前半段和後半段意思並不是很一致。按照前半段的意思,用的最多的(heavily used)最應該保留;而後半段,也即LRU的定義,反而是指“最久未使用”。假設這樣一種情況,記憶體只能容納兩個頁,如果考察的時間跨度大於2,對於0,0,...,0,1的頁面訪問序列,此時訪問頁面2,前半段會認為0的使用頻率最高,應該保留0,而後半段認為0是最久未被使用的,應該保留1。這樣就產生了矛盾。不過既然是一個近似,按後半段更合適一些,這樣反而顯得“最近最久未使用”是一個更合適的譯法。《現代作業系統》提到的3種演算法,其實都是符合定義的:
一種實現是用一個特殊連結串列,將最近最多使用的放在表頭,最近最少使用的放在表尾,每次使用到的頁面如果在連結串列中,就把它取出並放到表頭。不過這個實現一方面很耗時,並且實際上是“最近最久未使用”。
一種硬體實現是使用一個計數器,每次執行指令自增1,每個頁表項中提供一位來容納這個值,每次訪問時就把計數器的值存到訪問的頁表項中。淘汰頁面時選擇最小的即可。雖然很符合“最近最少使用”的含義,缺點是消耗了很多儲存空間;另外,計數器的溢位也是個問題。同樣是“最近最久未使用”。
另一種硬體實現則比較精巧。對於n個頁框的機器,提供一個n*n的矩陣,初始化為全0。當訪問頁框k時,將這個矩陣第k行全設為1,第k列全設為0,此時(k,k)是0。在任意時刻,哪一行的二進位制數最小,那麼它就是將被淘汰的頁面。可以發現,這種做法中,最後被訪問的頁面會把它所在的行變為最大的(k列全為0,代表此列的大小影響不計;僅有k行全1,必然最大)。同時,頁面的使用頻率越高,那麼它就能“保持”的較大。即使上文中探討的作為0,0,...0,1這種序列,仍然是保持1替換0,還是“最近最久未使用”。原書對這個實現的圖3-17:
(2)NFU演算法實現
LRU的實現比較複雜,而且可能需要藉助特殊的硬體。一種軟體實現被稱為NFU(Not Frequently Used,最不常用),每個頁面使用一個計數器,每次時鐘中斷時,將頁面的R位(是否被引用,0或1)加到計數器上。缺頁中斷時置換計數器值最小的。
這種實現的壞處是它“從不忘記任何事情”,簡單地說就是在之前計數器值比較高的頁面,即使不再訪問,仍然會保持這個值;而別的頁面在後續始終無法超過。對NFU做一個小修改,就可以很好的模擬LRU:將R位增加前先計數器右移、R位增加到計數器左邊的最高位而不是右邊的最低位。修改後的演算法稱為老化(aging)演算法。圖3-18是一個執行例項,其蘊含的特徵是:越高位越新,最近的使用權重最大;早期的使用記錄會隨著右移而捨棄。
從中也可看出NFU與LRU的第一個區別:對於(e),LRU只能從3和5中二選一,3和5都在2個時鐘前訪問過,而NFU會明確地喚出3。另一個區別是NFU只能追蹤有限次(相較於LRU的前兩種實現,要少一些),比如圖中的8次。前第9次和前1000次的訪問情況是無關緊要的。