不同類型的進程有不同的調度需求

第一種分類:

I/O密集型,等待I/O的時間用來處理其他進程

CPU密集型

第二種分類

批處理進程:不必與用户交互,不用很快響應

實時進程:有實習需求,響應時間要短、要穩定

交互式進程:需要經常與用户交互,需要較長時間等待用户輸入,響應時間要快,比如shell

 

linux支持普通的分時系統和實時系統

linux的調度基於分時和優先級。優先級是動態的,可以用設置nice。

進程調度通過schedule函數,進程調度的時機

1.中斷處理過程(包括時鐘中斷、I/O中斷、系統調用和異常)中,直接調用schedule(),或者返回用户態時根據need_resched標記調用schedule();

2.內核線程可以直接調用schedule()進行進程切換,也可以在中斷處理過程中進行調度,也就是説內核線程作為一類的特殊的進程可以主動調度,也可以被動調度;

3.用户態進程無法實現主動調度,僅能通過陷入內核態後的某個時機點進行調度,即在中斷處理過程中進行調度

 

中斷保存上下文和進程保存上下文不同

中斷:保存現場,恢復現場;在同一個進程中

進程:通過switch to的機制彙編實現;兩個進程的切換

進程上下文包括:用户地址空間(程序代碼、數據,用户堆棧等),控制信息(進程描述符、內核堆棧等),硬件上下文

 

進程調度通過schedule函數,下面分析schedule函數的執行過程

1 2865asmlinkage __visible void __sched schedule(void)//visible,表示在內核的部分都可以看到這個函數
2 2866{
3 2867    struct task_struct *tsk = current;
4 2868
5 2869    sched_submit_work(tsk);
6 2870    __schedule();
7 2871}

 調用__schedule

1 static void __sched __schedule(void)
 2 2771{
 3 2772    struct task_struct *prev, *next;
 4 2773    unsigned long *switch_count;
 5 2774    struct rq *rq;
 6 2775    int cpu;
 7 2776
 8 2777need_resched:
 9 2778    preempt_disable();
10 2779    cpu = smp_processor_id();
11 2780    rq = cpu_rq(cpu);
12 2781    rcu_note_context_switch(cpu);
13 2782    prev = rq->curr;
14 2783
15 2784    schedule_debug(prev);
16 2785
17 2786    if (sched_feat(HRTICK))
18 2787        hrtick_clear(rq);
19 2788
20 2789    /*
21 2790     * Make sure that signal_pending_state()->signal_pending() below
22 2791     * can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
23 2792     * done by the caller to avoid the race with signal_wake_up().
24 2793     */
25 2794    smp_mb__before_spinlock();
26 2795    raw_spin_lock_irq(&rq->lock);
27 2796
28 2797    switch_count = &prev->nivcsw;
29 2798    if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
30 2799        if (unlikely(signal_pending_state(prev->state, prev))) {
31 2800            prev->state = TASK_RUNNING;
32 2801        } else {
33 2802            deactivate_task(rq, prev, DEQUEUE_SLEEP);
34 2803            prev->on_rq = 0;
35 2804
36 2805            /*
37 2806             * If a worker went to sleep, notify and ask workqueue
38 2807             * whether it wants to wake up a task to maintain
39 2808             * concurrency.
40 2809             */
41 2810            if (prev->flags & PF_WQ_WORKER) {
42 2811                struct task_struct *to_wakeup;
43 2812
44 2813                to_wakeup = wq_worker_sleeping(prev, cpu);
45 2814                if (to_wakeup)
46 2815                    try_to_wake_up_local(to_wakeup);
47 2816            }
48 2817        }
49 2818        switch_count = &prev->nvcsw;
50 2819    }
51 2820
52 2821    if (task_on_rq_queued(prev) || rq->skip_clock_update < 0)
53 2822        update_rq_clock(rq);
54 2823
55 2824    next = pick_next_task(rq, prev);//選擇下一個進程
56 2825    clear_tsk_need_resched(prev);
57 2826    clear_preempt_need_resched();
58 2827    rq->skip_clock_update = 0;
59 2828
60 2829    if (likely(prev != next)) {
61 2830        rq->nr_switches++;
62 2831        rq->curr = next;
63 2832        ++*switch_count;
64 2833
65 2834        context_switch(rq, prev, next); /* unlocks the rq */
66 2835        /*
67 2836         * The context switch have flipped the stack from under us
68 2837         * and restored the local variables which were saved when
69 2838         * this task called schedule() in the past. prev == current
70 2839         * is still correct, but it can be moved to another cpu/rq.
71 2840         */
72 2841        cpu = smp_processor_id();
73 2842        rq = cpu_rq(cpu);
74 2843    } else
75 2844        raw_spin_unlock_irq(&rq->lock);
76 2845
77 2846    post_schedule(rq);
78 2847
79 2848    sched_preempt_enable_no_resched();
80 2849    if (need_resched())
81 2850        goto need_resched;
82 2851}

 next = pick_next_task(rq, prev)指明下一個進程,其中的調度策略被封裝了

 context_switch(rq, prev, next)用於完成上下文的切換

實驗:函數進程調度分析調試

 

apscheduler使用ProcessPoolExecutor調度結束後的資源釋放問題_堆棧

apscheduler使用ProcessPoolExecutor調度結束後的資源釋放問題_進程調度_02

使用gdb調試,設置斷點__schedule,context_switch,__switch_to

 

apscheduler使用ProcessPoolExecutor調度結束後的資源釋放問題_中斷處理_03

 執行到__schedule,根據next表示的進程,進行進程調用,通過context_switch切換上下文

apscheduler使用ProcessPoolExecutor調度結束後的資源釋放問題_進程調度_04

switch_to函數切換堆棧等