1. 程式人生 > >網易公開課《Linux核心分析》學習心得-理解程序排程時機跟蹤分析程序排程與程序切換的過程

網易公開課《Linux核心分析》學習心得-理解程序排程時機跟蹤分析程序排程與程序切換的過程

首先在核心程式碼中搜索schedule,發現以下結果
這裡寫圖片描述
在core.c檔案中是
這裡寫圖片描述

實驗

設定斷點
這裡寫圖片描述
跟蹤schedule的程序
這裡寫圖片描述
可以看到

struct task_struct *tsk = current;
sched_submit_work(tsk);

將一個task_struct的指標賦值給了當前程序,之後呼叫了sched_submit_work。
繼續執行,進入後面的__schedule()函式
這裡寫圖片描述
可以看到切換程序的大部分程式碼都在__schedule()函式中
執行完畢
這裡寫圖片描述

分析

程序排程的時機

• 中斷處理過程中,直接呼叫schedule(),或者返回使用者態時根據need_resched標記呼叫schedule()。
• 核心執行緒可以直接呼叫schedule()進行程序切換,也可以在中斷處理過程中進行排程,也就是說核心執行緒作為一類的特殊的程序可以主動排程,也可以被動排程。
• 使用者態程序無法實現主動排程,僅能通過陷入核心態後的某個時機點進行排程,即在中斷處理過程中進行排程。

switch_to的分析

switch_to巨集有三個引數,它們是prev,next和last。prev表示替換程序,next表示新程序描述符放入賦值在記憶體中的位置。最後一個引數的輸入引數,它表示巨集把程序C的描述符地址寫在記憶體的位置。

在程序切換之前,巨集吧第一個輸入引數prev表示的變數的內容存入CPU的暫存器,在完成程序切換,A已經恢復執行,巨集把CPU的eax的暫存器的內容寫入由第三個輸入引數(last所示的A在記憶體中的位置)。因為CPU的eax暫存器不會在切換點發生變化,所以C得描述符地址也存在記憶體的位置。在schedule()執行過程中。引數last指向A的區域性變數prev,所以prev被C的地址覆蓋。
這裡寫圖片描述

__schedule()的分析

static void __sched __schedule(void)
2771{
// 建立一些區域性變數
2772    struct task_struct *prev, *next;
2773    unsigned long *switch_count;
2774    struct rq *rq;
2775    int cpu;
2776
// 關閉程序搶佔
2777need_resched:
2778    preempt_disable();
// 初始化一些變數
2779    cpu = smp_processor_id();
2780    rq = cpu_rq(cpu);
2781
rcu_note_context_switch(cpu); 2782 prev = rq->curr;
// 選擇一個高優先順序的任務加入佇列
2824    next = pick_next_task(rq, prev);
//並把這個任務的需要加入的標誌need_resched去掉
2825    clear_tsk_need_resched(prev);
2826    clear_preempt_need_resched();
2827    rq->skip_clock_update = 0;
2828
//完成整個排程活動
2829    if (likely(prev != next)) {
2830        rq->nr_switches++;
2831        rq->curr = next;
2832        ++*switch_count;
2833
2834        context_switch(rq, prev, next); /* unlocks the rq */
2835        /*
2836         * The context switch have flipped the stack from under us
2837         * and restored the local variables which were saved when
2838         * this task called schedule() in the past. prev == current
2839         * is still correct, but it can be moved to another cpu/rq.
2840         */
2841        cpu = smp_processor_id();
2842        rq = cpu_rq(cpu);
2843    } else
2844        raw_spin_unlock_irq(&rq->lock);
2845
2846    post_schedule(rq);
2847
2848    sched_preempt_enable_no_resched();
2849    if (need_resched())
2850        goto need_resched;
2851}
2852
2853static inline void sched_submit_work(struct task_struct *tsk)
2854{
2855    if (!tsk->state || tsk_is_pi_blocked(tsk))
2856        return;
2857    /*
2858     * If we are going to sleep and we have plugged IO queued,
2859     * make sure to submit it to avoid deadlocks.
2860     */
2861    if (blk_needs_flush_plug(tsk))
2862        blk_schedule_flush_plug(tsk);
2863}

總結

Linux系統的一般執行過程是首先有正在執行的使用者態程序X然後在停止前發生中斷,使用SAVE_ALL儲存現場。並且中斷處理過程中或中斷返回前呼叫了schedule(),其中的switch_to做了關鍵的程序上下文切換。最後,標號1之後開始執行使用者態程序Y(這裡Y曾經通過以上步驟被切換出去過因此可以從標號1繼續執行)。
完成排程恢復現場,之後繼續執行使用者態程序Y。