內存管理


預備

地址的生成

物理地址

首先一個內存地址代表一個物理內存中一個內存單元的存儲空間。

例如:

地址能表示的范圍和cpu有關,如果cpu是32位的,按字節編址的話。

首地址是:0,

最后的地址是:2^32 - 1,

地址的個數是:2^32,

內存大小是:地址的個數 * 8(字節的大小) = 4GB,也就是說如果cpu只有32位大小的話,那么尋址空間只有4GB的大小,如果內存超過4GB那么超過4GB的內存空間會被浪費掉,cpu無法查找那么大的內存地址。

邏輯地址

在編程語言中,一個變量是邏輯地址,在機器語言中,一個01序列是一個邏輯地址(因為實際地址要等於邏輯地址+基址寄存器中的值)。

如果要將邏輯地址變為物理地址,我們要通過cpu中的內存管理單元(MMU),MMU提供將邏輯地址通過某種方式映射為物理地址功能。

邏輯地址 ——》物理地址的過程:

內存的分配

連續內存分配

連續內存的分配指的是給一個程序分配一塊連續的內存地址空間。這個內存地址空間的大小符合程序所需要的大小。可以過大,但是不能太小。

空閑空間的管理:位圖法,和鏈表法。

  • 位圖法: 將內存划分成N個分配單元,每個單元在用位圖中的一位表示,如果該單元被使用則為1,如果未被使用則為0。
  • 鏈表法: 維護一個由空閑區和進程區表示的鏈表,其中鏈表項由空閑和進程指示標志和起始地址還有內存塊大小表示。

分配內存的方法:

載入程序
	first fit: 從內存地址的首地址(index:0)開始向后查找,如果找到一塊地址空間足夠大,可以分配給該程				序,那么就將該程序裝入該地址空間。

	worst fit: 將空閑的內存地址空間排序,從這個空閑內存地址空間序列中的頭部(最大空閑塊)開始給程序分配內			   存地址空間。

	best fit: 從內存地址空間的首地址開始向后查找,找到一塊地址空間和該程序所需內存空間最適合的空閑塊,分				  配給該程序。

程序申請存放要用的值:程序的堆棧段。

連續地址空間的分配方式會產生內存空間碎片,就是有些內存空間太小,小到不足以存放任何程序,而且這些小內存分布在內存的各個地方。

內存空間碎片分為:

  • 內碎片:就是分配給程序,但是程序沒有使用的空間。
  • 外碎片:各個程序地址空間之間的內存地址空間。

內存碎片的處理方法

swap:如果一個程序的地址空間不夠用了,那么就將內存中的一個未被使用到的內存放入磁盤的交換區。為該程 序提供一個更大的地址空間

壓縮算法(memory compaction):就是將內存中的程序相鄰存放,將內存碎片合成一個更大的內存地址空間。

非連續內存的分配

分頁

分頁是虛擬內存系統中的一種技術。將程序擁有的內存空間划分為N個大小相同的地址空間,這個塊被稱為,每個頁都有自己的連續地址范圍,這些頁可以被映射到物理地址空間中。

映射過程:

MMU映射的方式是通過頁表來查找page frame number在加上offset位生成物理地址一個頁包含多個單元地址空間,通過offset指出是頁中的哪個地址空間。

所以我們可以看出一個頁的結構由頁號加上偏移位。

物理內存中與頁所對應的連續內存地址塊被稱為page frame。

頁中的offset和page frame的offset大小相同,offset指的是頁大小。

所以虛擬地址轉換位物理地址的過程:

​ MMU頁表寄存器找到頁表的存放的首地址,通過虛擬頁號找到page frame number通過與offset相加得到物理地址。

使用虛擬內存還會遇到兩個問題,一個是時間,還有一個是空間。

時間指的是:頁表通常存放在主存中,不在cache中,如果為了訪問內存中的數據,就要訪問內存兩次(一次是訪問頁表,一次訪問內存數據)這樣虛擬內存並未對計算機設計有多大的幫助。

空間指的是:如果虛擬地址有64位,而offset只有14位,那么存放一個頁表就需要2^50 * 50 / 8 = 6 400Tib的內存空間來存放頁表,這是不可能的。

我們用TLB(快表)來存放經常訪問的幾個頁表項。

用多級頁表和反向頁表解決頁表過大的問題(具體就說將用到的虛擬地址空間存入虛擬內存中,不用的先不存)。

這部分的內容在p112 ~ 116。

虛擬內存

由於程序對內存容量需求的增長快於內存容量的增長,為了使得計算機可以並發運行多個程序,但是要解決內存不足的問題,虛擬內存技術就出現了。

在虛擬內存之前,還有兩個解決內存不足的方法

  • overlay(覆蓋)

    overlay需要程序員將一個程序划分成多個代碼塊,多個代碼塊之前分組公用一個代碼塊,但是被調代碼塊不能夠與調用的代碼塊公用一塊內存塊,所以overlay還需要程序員指定好每個代碼塊的內存塊。
    
    在代碼塊被換出內存時,操作系統要保護好被換出代碼塊的所有信息。待該代碼塊換入內存時,能夠恢復被換出前的所有狀態,不影響接下去的執行。
    
    具體的如下圖所示。
    

  • swap

    swap就是將程序載入內存,待程序執行一段時間后,將程序換出到磁盤中去,主存中只保留需要執行的進程,而空閑進程就保留在磁盤中。
    

虛擬內存技術的出現:

overlay需要程序的支持,會給程序員的編碼帶來額外的工作和負擔,而swap技術如果進程的內存容量大那么換入換出的成本太高了。虛擬內存技術可以將原本需要程序員來做的程序分塊交由操作系統來完成,也是通過swap方法,但是不是大塊程序的換入換出,而是更加細小的程序塊換入換出。這個內存塊被稱為頁,分頁技術也是目前實現虛擬內存的主要技術。

虛擬內存的特征:

  • 大空間

    每個程序都有自己的虛擬內存地址空間,虛擬內存地址空間 = 主存 + 外存。

  • 部分交換

    將目前執行程序,需要用的頁載入內存,不需要的頁換出外存(只有當內存空間滿了,但是又要載入頁的時候才將內存中不需要的頁換出)

  • 不連續性

    虛擬內存技術是,每個程序都有連續的虛擬內存地址,但是虛擬內存地址映射到物理內存上的時候是不一定連續的。這個也解決了內存碎片的問題。

缺頁(page fault)處理:

頁表項說明:

一個頁表項由Valid、dirty、ref、物理內存地址組成。

valid:表示這個虛擬內存所對應的物理內存存不存在。

dirty:表示這個虛擬內存所對應的物理內存又沒有被重寫。當這個位1時,這個物理內存要從內存地址中置換出去時,我們要先把數據寫回外存才可以清空這物理內存的信息,將新頁置換進來。

ref:這個虛擬內存所對應的物理內存最近有沒有被引用。這個位是為置換算法服務的。這個位隔一段時間就被置0,當被cpu訪問的時候被置1。

cpu將邏輯地址(虛擬內存地址)提交給MMU,MMU首先查看TLB,如果TLB有所對應的內存地址,就之間訪問該內存,取出數據.如果沒有所對應的內存地址,MMU就去訪問內存中的page table,如果page table有對應的物理內存地址,那就將該頁表現換入快表中(如果TLB中的dirty位為1時,我們要先將該項的dirty位寫回內存所對應的頁表項中),如果沒有的話,cpu就發出異常,操作系統根據異常提供邏輯地址的信息,去外存中找到相應的頁換入內存中去(如果內存中的dirty位為1時,就要先將數據寫回外存).

頁換出的位置:

如果時程序文件,就是換出到程序所在的外存位置.
如果時程序運行過程中的臨時數據,就換出到操作系統的swap空間中去.

置換算法

最近未被使用(NRU)

NRU算法根據頁表中的頁表項的ref位和dirty位將所有頁表了分為四類:

ref dirty
0 0 沒被訪問和沒被修改類
0 1 沒被訪問但是被修改類
1 0 被訪問,但是沒被修改類
1 1 即被訪問,也被修改類

當每次缺頁中斷發生時,操作系統檢查頁表中的所有頁表項,隨機挑一個編號(表中,由ref和dirty位組成的2位二進制表示的十進制數的編號)最小的頁表項替換出去。其中ref位就是訪問位會定期的清零。

如果硬件沒有提供ref位和dirty位,那么根據缺頁異常來設置內部表中的ref位和dirty位。例如:讀取某個頁面發生缺頁異常時,操作系統捕獲異常,設置內部表的R位,並添加頁表項指向正確的頁面,設置該頁為只讀模式,如果寫入某個頁面發生缺頁異常,那么操作系統設置W為,設置該頁為可讀可寫頁。

先入先出(FIFO)

操作系統維護一個鏈表,根據頁表的換入內存的時間為順序,越早換入內存的頁表越靠近表頭,越晚換入內存的鏈表越靠近表尾,當發生缺頁異常時,操作系統將表頭的頁面換入外存,並將新的頁面換入主存。

第二次機會法

第二次機會算法是對FIFO算法的一個優化,因為一個表頭的頁面可能是一個經常訪問的頁面,雖然他最早換入內存,如果它被換出去,由於經常訪問的原因,后續指令可能很快又需要訪問該頁面導致缺頁異常。那么我們就要設置相應的機制,不讓它替換出去。

那就是當缺頁異常發生時,將表頭的替換出內存時,我們要檢查該頁的ref位,如果該ref位是1,那么就證明該頁還有被引用,因此我們將該頁換到表尾,然后檢查下一頁,直到ref為0,將缺失的頁再載入到內存中去,並加到表尾。

時鍾頁面置換算法

二次機會算法要做鏈表項的移動處理,移動處理不僅沒有必要還降低算法的效率。時鍾頁面置換算法將鏈表的表頭表尾相聯,使用一個指向鏈表項的指針,該指針指向最早加入鏈表的頁。當發生缺頁異常時,操作系統就檢查指針所指的頁面,如果ref位為0就清除該頁,並將新頁替換到該位置,指向指向下一個頁。如果ref位為1就將ref設置為0並將指針指向下一位,直到找到一個ref位為0的頁。

最近最少使用頁面置換算法(LRU)

LRU算法的基礎是程序具有局部性原理,因為LRU是根據歷史推測未來,如果一個頁最近一直被使用,那么它就符合時間局部性原理,未來它還可能接着被使用,那么我們不能將該頁置換出去,如果一個頁最近使用的頻率很低,那么就不符合時間局部性原理。未來可能不會再被使用,那么我們就可以將該頁替換出去。如果程序不具有局部性,那么LRU的推測將毫無作用,因為LRU是假定程序具有局部性原理的前提下的一個算法。

但是LRU的算法實現代價很高,因為LRU需要維護一個鏈表,使用頻率越高的頁越靠近表頭,使用頻率越低的頁越靠近表尾。那么這就需要大量的鏈表操作。因為沒訪問一個頁,記錄頁的使用頻率的值就要加一,這個值一變化就要調整鏈表的順序。

最不常用置換算法(NFU)

每個頁都有一個相應的軟件計數器關聯,每次時鍾中斷時,操作系統掃描一遍內存,將ref位與相關聯的計算器相加,當產生缺頁異常的時候,操作系統將計數器值最低的那個頁替換出去。

NFU的算法有一個問題就是,當一個頁曾經很長的一段時間頻繁使用,那么這個頁的計數器值很高,導致后來這個頁可能不使用了,但是這個值依然很高,它還是會存放在內存中。然后將有用的頁給替換出去。

老化算法

老化算法是對NFU算法的一種優化。

首先每次時鍾中斷,ref位和頁相應的計算器相加的時候,先將計數器右移一位,然后將ref位的值和最低位相加。

老化算法就是定時定時減少每個頁訪問次數計數的值,就像NFU算法的問題一樣,一個頁面一段時間被進程訪問,但是后面不訪問了,后面的頁面訪問的頻率並不會都比這個頁面高,所以頁面常駐內存中,當時老化算法就是針對這一個情況,如果一個頁面后面不訪問了,那么它將一致衰減直到0,而后面的頁面只要訪問最少累計訪問次數也會是1就不會被換出內存。

工作集頁面置換算法

工作集:就是進程目前正在使用頁面的一個集合。

常駐集:就是進程目前需要的頁面在內存中的集合。

也就是說,如果給進程分配的內存足夠大的時候,工作集的頁面都可以載入內存中,此時常駐集等於工作集,那么缺頁異常的概率就會降低。如果給進程分配的內存不足以裝下所有頁面,那么常駐集就是工作集的一個真子集。

如果常駐集的元素遠小於工作集,那么程序很容易發生抖動。因為工作集代表的是進程當前時刻需要訪問的內存,而常駐再內存的頁又太少,需要頻繁的做換入換出的操作。例如,工作集是0,1,2,3。常駐集是1,2。如果內存中存在在頁是0、1那么下一條指令可能需要頁2,就要置換出頁0或者頁1。同理,一條可能需要3,那么又得置換,如果再下一條又需要0。那么此時0已經被置換出內存,需要再置換入內存。大部分時間都花在了io身上。導致進程執行時間很長。

抖動:如果一個進程運行時間(io時間和cpu時間)絕大多數都是io時間,那么這個就是抖動現象,也就是說缺頁率太高導致要頻繁的執行頁面置換工作。

工作集的生成

程序要具有局部性,工作集才好用,如果不具有局部性,那么工作集和頁都要頻繁的更改.
  1. 規定一個數值k,將當前指令訪問的頁與之前指令訪問的(k-1)個頁為一組。將這些頁提取出來去除重復頁,生成的就是一個工作集。

    具體方法就是使用一個k大小移位寄存器, 這個寄存器存放進程最近訪問過的k個頁面,當進程每做一次內存訪問,就將移位寄存器左移一位,並將新頁放入寄存器中,當缺頁中斷發生時,將不在工作集中的頁換出,並將移位寄存器的值提取出來,刪除重復元素,排序生成工作集。
    
  2. 還有一種是基於時間生成的工作集。規定一個時間間隔。

工作集的置換過程

當缺頁發生時,操作系統掃描內存。

如果ref == 1將新的訪問時間寫入,替換舊的訪問時間。

如果ref == 0 但是訪問時間在規定的時間間隔內,就保留該頁面。

如果ref == 0 但是訪問時間超過了規定時間間隔,就換出該頁。

如果所有頁面都在規定的時間間隔內,也就是都在工作集中,那么選擇一個ref = 0 並且最早換入的頁。

如果ref都等於一,那么就隨機替換一個,最好時干凈的頁。

工作集時鍾頁面置換算法

與時鍾算法一樣,但是加入了工作集的限制。

首先將指針指向最早加入鏈表的頁,如果引發缺頁中斷,那么檢查該頁

如果ref == 0如果在間隔內,就不適合被淘汰,就跳過該頁,

如果等於ref == 1就將ref = 0然后指向下一頁。

如果ref == 0並且不在時間間隔內,dirty == 0就替換該頁,將新頁置換進來

如果ref == 0並且不在時間間隔內,但是如果dirty == 1,那么也要跳過該頁,去尋找一個舊的可以替換的頁。寫回操作可能在跳過后某個時鍾周期完成了。完成寫回后dirty = 0,那么下次檢查時,就可以替換該頁了。

局部性策略和全局策略

以上算法均可用於全局策略或者局部策略。

  • 局部策略

    局部策略只針對操作系統給該進程分配的物理地址空間上進行操作,如果缺頁了被替換的頁還是該進程的物理地址空間的頁。
    
  • 全局策略

    全局策略是針對整個內存進行頁面替換操作,就是說,被替換的頁是該進程地址空間內的頁,也可以上是其他進程地址空間內的頁。
    

    在全局策略上進行的算法可以動態的調控每個進程所需要的物理頁幀的大小,降低抖動現象的產生,因為需要大物理地址空間的頁我們動態的給它分配更多的物理地址空間,通過減少其他進程的物理地址空間,依據缺頁中斷率(PFF)算法。

    PFF算法:當一個進程的缺頁終端率大於一個閾值時,我們就要給該進程增加物理地址空間,當低於一個閾值時,減少它的物理地址空間.
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM