進程調度之7:need_resched與強制調度

date: 2014-11-02 13:16linux

在《進程的調度與切換》一節中,咱們提到,強制調度的兩個條件:安全

  1. 調度時機:系統調用返回到用戶空間前夕,以及中斷或者異常服務程序返回到用戶空間前夕。可能會有人擔憂以下兩種狀況:其一若是進程「躲在」「安全地帶」內核空間中不出來,調度器豈不僅能乾着急?好在內核的設計與實現避免了這個問題。其二:若是進程在用戶空間運行,既不調用系統調用函數,也沒有中斷與異常發生,豈不是也沒法進行強轉調度?別忘了,系統的時鐘中斷在默默地堅守着哩。函數

  2. 必要條件:當前進程的need_resched字段必須非0。該字段必須由內核去設置,爲了讓調度器有效運轉起來,內核必須「瞅準時機」「見縫插針」地設置該字段。設置的時機包括:this

    • 其一:在時鐘中斷服務程序中,當發現當前進程連續運行太長時間時;
    • 其二:當喚醒一個睡眠中的進程,發現被喚醒的進程比當前進程更有資格運行時;
    • 其三:一個進程經過系統調用改變調度政策(sched_setscheduler)或表示禮讓(sched_yield)時。這種狀況實際上應該被視爲主動的、自願的調度,由於這些系統調用在返回用戶空間時會引發當即調度(而上面兩種狀況只是設置好了need_resched字段,至於什麼時候能有「調度時機」仍是個未知數)。

1 時鐘中斷

時鐘中斷服務程序do_timer_interrupt()中調用do_timer(),對單CPU結構後者調用update_process_times()來調整當期進程與時間相關的一些運行參數,代碼在<kernel/timer.c>中:spa

/*
     * Called from the timer interrupt handler to charge one tick to the current 
     * process.  user_tick is 1 if the tick is user time, 0 for system.
     */
    void update_process_times(int user_tick)
    {
    	struct task_struct *p = current;
    	int cpu = smp_processor_id(), system = user_tick ^ 1;
    
    	update_one_process(p, user_tick, system, cpu);
    	if (p->pid) {
    		if (--p->counter <= 0) {
    			p->counter = 0;
    			p->need_resched = 1;
    		}
    		if (p->nice > 0)
    			kstat.per_cpu_nice[cpu] += user_tick;
    		else
    			kstat.per_cpu_user[cpu] += user_tick;
    		kstat.per_cpu_system[cpu] += system;
    	} else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
    		kstat.per_cpu_system[cpu] += system;
    }

2 wake_up_process()喚醒一個進程

<kerne./sched.c>
    
    /*
     * Wake up a process. Put it on the run-queue if it's not
     * already there.  The "current" process is always on the
     * run-queue (except when the actual re-schedule is in
     * progress), and as such you're allowed to do the simpler
     * "current->state = TASK_RUNNING" to mark yourself runnable
     * without the overhead of this.
     */
    inline void wake_up_process(struct task_struct * p)
    {
    	unsigned long flags;
    
    	/*
    	 * We want the common case fall through straight, thus the goto.
    	 */
    	spin_lock_irqsave(&runqueue_lock, flags);
    	p->state = TASK_RUNNING;
    	if (task_on_runqueue(p))
    		goto out;
    	add_to_runqueue(p);
    	reschedule_idle(p);
    out:
    	spin_unlock_irqrestore(&runqueue_lock, flags);
    }

可見,喚醒一個進程只是將它的狀態改成TASK_RUNNING而後加入可運行隊列。設計

reschedule_idle()判斷被喚醒的進程是否比當前進程更有資格運行,若是是,則設置need_resched字段,代碼以下:rest

/*
     * the 'goodness value' of replacing a process on a given CPU.
     * positive value means 'replace', zero or negative means 'dont'.
     */
    static inline int preemption_goodness(struct task_struct * prev, struct task_struct * p, int cpu)
    {
    	return goodness(p, cpu, prev->active_mm) – 
                   goodness(prev, cpu, prev->active_mm);
    }
    
    
    static void reschedule_idle(struct task_struct * p)
    {
    #ifdef CONFIG_SMP
        ...
    #else /* UP */
    	int this_cpu = smp_processor_id();
    	struct task_struct *tsk;
    
    	tsk = cpu_curr(this_cpu);
    	if (preemption_goodness(tsk, p, this_cpu) > 1)
    		tsk->need_resched = 1;
    #endif
    }

3 改變調度政策和「禮讓」

用戶登陸到系統後,第一個進程的使用調度政策爲SCHED_OTHER,即無實時要求的交互式引用。其後,經過fork建立子進程時,則將此調度政策遺傳給子進程。但在子進程中能夠經過sched_setscheduler()來改變調度政策或者調用sched_setparam()函數來改變實時調度政策的優先級。它們的原型爲:code

int sched_setscheduler(pid_t pid, int policy, struct sched_param *);
    int sched_setparam(pid_t pid, struct sched_param *);

結構體sched_param表示調度參數,只有一個成員sched_priority表示實時優先級,取值範圍爲[0, 99]。對於SCHED_OTHER政策來講,該值必須爲0。隊列

struct sched_param {
	    int sched_priority;
    };

這兩個系統調用內核中都是調用setscheduler(),代碼在<linux/sched.c>中,代碼比較簡單,這裏不贅述。這裏說明一點:若是pid所表明的進程在可執行隊列中,那麼這兩個系統調用會將它們挪到可執行隊列的隊首,使得再下次調度時佔優,而後將當前進程的need_resched字段置1,當即啓動一次調度。進程

至於sched_yield(),使當前進程爲其餘進程「讓路」,但當前進程並無睡眠,也沒有改變當前進程在可執行隊列中的位置。sched_yield()只是經過設置當前進程的need_resched字段來啓動一次調度。注意,只有在當前進程的調度政策爲SCHED_OTHER時,纔會在調度政策上設置SCHED_YIELD標誌,使得下一次調度將不會再調度該進程。但在下一次調度以後,在__schedule_tail函數中會清除SCHED_YIELD標誌,還進程「自由之身」。

與主動調度不一樣,強制調度在適當的時機將當前進程的need_resched字段置1,而後「眼巴巴」地等待調度時間的到來。也就是發現有調度的必要到調度真正發生有一個延遲,叫作調度延遲(dispatch latency)。

相關文章
相關標籤/搜索