1. 程式人生 > >記憶體管理與程序排程

記憶體管理與程序排程

一、記憶體管理

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