記憶體管理與程序排程
一、記憶體管理
1、核心
使用alloc_pages申請物理頁幀
使用kmalloc申請專用/通用記憶體塊、vmalloc申請物理不連續,邏輯相連記憶體
使用kmap建立高階地址對映
外碎片:空閒記憶體零散分佈,無法滿足大記憶體需求(夥伴系統解決)
內碎片:申請一塊記憶體真正使用的只有小部分(slab,通用記憶體塊解決)
2、使用者程序
1)申請記憶體
使用malloc動態分配,分配的記憶體並不是立即調入,而是擁有該記憶體的訪問權,等到真正訪問時,引發缺頁異常調入
malloc為glibc庫函式,實際使用brk/mmap系統呼叫
申請記憶體<128k時,呼叫brk,簡單移動堆指標
>128時呼叫mmap進行記憶體對映 邊界值可通過mallopt進行設定
free一段記憶體並不是馬上還給os,通過malloc依次分配A、B、C三塊記憶體,free A,堆指標無法收縮,保留A供下次呼叫malloc時使用;free B C(B+C 超過 128k)此時才收縮堆指標,將C這段記憶體還給os
2)訪問記憶體
cpu引用一個虛擬地址,TLB命中,直接得到實體地址
未命中,通過cr3暫存器存放全域性目錄地址,一層層索引+偏移得到實體地址
將實體地址傳送給快取記憶體,快取命中直接得到對應資料,未命中則繼續訪問下級快取或直接訪問記憶體
3、核心與使用者空間的資料傳遞
通過暫存器
核心不會直接引用使用者記憶體,通過get_user/put_user、copy_from_user/copy_to_user等巨集進行拷貝
二、程序排程
1、核心棧與程序描述符
根據sp快速獲取到程序描述符
2、排程規則
總的來說實時程序就是大爺,優先順序高的一直執行,同優先順序的再根據FIFO(先到先得)、RoundRobin(輪轉)的規則
沒有就緒實時程序才輪到普通程序,普通程序目前採用CFS排程演算法
3、排程框架
1)主排程器 主動讓出cpu
2)週期排程器 時間中斷更新統計資訊,設定是否需要排程的標誌,中斷返回時根據標誌呼叫主排程器
4、虛擬執行時間
將程序優先順序轉換成權重,相鄰優先順序間的比重相差10%
延遲排程週期,預設20ms,在這個時間段內所有就緒程序至少執行一次
A程序執行1ms(-1優先順序),B程序執行1ms(1優先順序),A的vruntime為0.9,B的vruntime為1.1
cfs_rq還有一個min_vruntime,是這個佇列執行的一個基準。表示這個佇列之前執行的程序中,執行一次花得最長的時間
紅黑樹的key值是每個排程實體的vruntime減去min_vruntime
程序排程虛擬碼:
def schedule():
'主排程器框架'
while True:
preempt_disable() # '排程開始前先關核心搶佔,避免丟失程序資訊'
__schedule()
preempt_enable()
if(not need_resched()):
break
def __schedule():
next = pick_next()
if(next != curr):
switch_mm() # 切換虛擬記憶體、重新整理TLB、快取失效
switch_to() # 儲存當前核心棧、暫存器資訊,恢復即將執行程序的狀態
def pick_next():
if curr.sched_class == cfs and cfs_rq.nrunning == rq.nrunning:
# 呼叫cfs的pick_next
return cfs_pick_next()
else:
# 遍歷所有排程器
for sched_class in (rt_clss, cfs_class, idle_class):
p = sched_class.pick_next()
if p:
return p
def cfs_pick_next():
p = min(curr, first, second) # first紅黑樹最左,second左邊第二
p = min(p, last, next) # 最近一次喚醒成功、失敗的程序
同一個程序中的執行緒間切換,少了switch_mm
不會造成頁目錄切換,快取失效,切換代價相對較小
switch_to之後已經切換到其他程序了,A 切換到 B 再由 C切換回A時,用兩個引數A不知道上一個程序是誰,但需要可能A需要替C收屍,所以 switch_to有三個引數prev、next、last