天天看點

linux nice值_嵌入式Linux中程序排程怎樣來解析

合作微信:xydf321456

Linux是一套免費使用和自由傳播的類Unix作業系統,是一個基于POSIX和UNIX的多使用者、多任務、支援多線程和多CPU的作業系統。它能運作主要的UNIX工具軟體、應用程式和網絡協定。

1.前言

處理機(CPU)是整個計算機系統的核心資源,在多程序的作業系統中,程序數往往多于處理機數,這将導緻各程序互相争奪處理機。程序排程對系統功能的實作 及各方面的性能都有着決定性的影響,其實質就是把處理機公平、合理、高效地配置設定給各個程序。排程是實作多任務并發執行的必要手段,不同的作業系統有着不同 的排程目标。在傳統的Unix類分時系統中,保證多個程序公平地使用系統資源,提供較好的響應時間是排程的主要目标;而在強實時作業系統中,總是優先級高 的任務優先獲得處理機的使用權。

linux nice值_嵌入式Linux中程式排程怎樣來解析

Linux具有核心穩定、功能強大、可裁減、低成本等特點,非常适合嵌入式應用。但是Linux核心本身并不具備 強實時特性,且核心體積較大,是以,想要把Linux用于嵌入式系統,必須對Linux進行實時化、嵌入式化。Linux結合實時程序和非實時程序(普通 程序)自身的特點,綜合了上述幾種排程政策,實作了高效、靈活的程序排程。

2.Linux程序排程分析

2.1 Linux程序狀态的描述

Linux将程序狀态描述為如下五種:

TASK_RUNNING:可運作狀态。處于該狀态的程序可以被排程執行而成為目前程序。

TASK_INTERRUPTIBLE:可中斷的睡眠狀态。處于該狀态的程序在所需資源有效時被喚醒,也可以通過信号或定時中斷喚醒。

TASK_UNINTERRUPTIBLE:不可中斷的睡眠狀态。處于該狀态的程序僅當所需資源有效時被喚醒。

TASK_ZOMBIE:僵屍狀态。表示程序結束且已釋放資源,但其task_STruct仍未釋放。

TASK_STOPPED:暫停狀态。處于該狀态的程序通過其他程序的信号才能被喚醒。

2.2 排程方式

Linux中的每個程序都配置設定有一個相對獨立的虛拟位址空間。該虛存空間分為兩部分:使用者空間包含了程序本身的代碼和資料;核心空間包含了作業系統的代碼和資料。

Linux采用“有條件的可剝奪”排程方式。對于普通程序,當其時間片結束時,排程程式挑選出下一個處于TASK_RUNNING狀态的程序作為目前程序 (自願排程)。對于實時程序,若其優先級足夠高,則會從目前的運作程序中搶占CPU成為新的目前程序(強制排程)。發生強制排程時,若程序在使用者空間中運 行,就會直接被剝奪CPU;若程序在核心空間中運作,即使迫切需要其放棄CPU,也仍要等到從它系統空間傳回的前夕才被剝奪CPU。

3.排程政策

3.1 三種排程政策

(1)SCHED_OTHER。SCHED_OTHER是面向普通程序的時間片輪轉政策。采用該政策時,系統為處于TASK_RUNNING狀态的每個程序配置設定一個時間片。當時間片用完時,程序排程程式再選擇下一個優先級相對較高的程序,并授予CPU使用權。

(2)SCHED_FIFO。SCHED_FIFO政策适用于對響應時間要求比較高,運作所需時間比較短的實時程序。采用該政策時,各實時程序按其進入可 運作隊列的順序依次獲得CPU。除了因等待某個事件主動放棄CPU,或者出現優先級更高的程序而剝奪其CPU之外,該程序将一直占用CPU運作。

(3)SCHED_RR。SCHED_RR政策适用于對響應時間要求比較高,運作所需時間比較長的實時程序。采用該政策時,各實時程序按時間片輪流使用CPU。當一個運作程序的時間片用完後,程序排程程式停止其運作并将其置于可運作隊列的末尾。

3.2 程序排程依據

Linux隻有一個可運作隊列,處于TASK_RUNNING狀态的實時程序和普通程序都加入到這個可運作隊列中。Linux的程序排程采用了動态優先級 和權值調控的方法,既可實作上述三種排程政策,又能保證明時程序總是比普通程序優先使用CPU。描述程序的資料結構task_struct中用以下幾個數 據作為排程依據:

Struct task_struct {

……

volaTIle lONg need_resched; long counter;

long nice; unsigned long policy;

unsigned long rt_priority; ……

};

counter的值是動态變化的,程序運作時,每一個時鐘滴答後,其值減1。當counter值為0時,表示該程序時間片已用完,該程序回到可運作隊列中,等待再次排程。

為保證明時程序優于普通程序,Linux采取權重處理法。在程序排程過程中,每次選取下一個運作程序時,排程程式首先給可運作隊列中的每個程序賦予一個權 值weight。普通程序的權值就是其counter和優先級nice的綜合,而實時程序的權值是它的rt_priority的值加1000,確定實時進 程的權值總能大于普通程序。排程程式檢查可運作隊列中所有程序的權值,選取權值最大者作為下一個運作程序,保證了實時 程序優先于普通程序獲得CPU。 Linux使用核心函數goodness()對程序進行權重處理:

StaTIc inline goodness (struct task_struct * pint this_cpu, struct mm_struct *this_mm){

Int weight;

Weight=-1;

If (p-》policy & SCHED_YIELD)

goto out;

If (p-》policy==SCHED_OTHER){

weight=p-》counter; If (! weight)

Goto out;

#Ifdef CONFIG_SMP

If (p-》processor==this_cpu)

Weight+=PROC_CHANGE_PENALTY;

#Endif

If (p-》mm==this_mm||! p-》mm)

Weight+=1;

Weight+=20-p-》nice;

Goto out;

}

Weight=1000+p-》rt_priority;

Out:

return weight;

}

從goodness()函數可以看出,對于普通程序,其權值主要取決于剩餘的時間配額和nice兩個因素。nice的規定取值範圍為19~-20,隻有特 權使用者才能把nice值設為負數,而表達式(20-p-》nice)掉轉方向成為1~40。是以,綜合的權值在時間片尚未用完時基本上是兩者之和。 如果是核心程序,或者其使用者空間與目前程序相同,則權值将額外加1作為獎勵。對于實時程序,其權值為1000+p-》rt_priority,當 p-》counter達到0時該程序将移到隊列的尾部,但其優先級仍不少于1000。可見當有實時程序就緒時,普通程序是沒機會運作的。

由此可以看出,通過goodness()函數,Linux從優先考慮實時程序出發,實作了多種排程政策的統一處理,其設計思想可謂非常巧妙。

3.3 程序排程

Linux的程序排程由排程程式schedule()完成,通過對schedule()的分析能更好了解排程的過程。schedule()首先判斷目前運作程序是否具有SCHED_RR 标志,本文取一部分加以分析:

if (prev-》policy==SCHED_RR) Goto move_rr_last;

……

Move_rr_last:

If (! prev-》counter){ Prev-》counter=NICE_TO_TICKS (prev-》nice);Move_last_runqueue (prev);

}

Goto move_rr_back;

prev-》counter代表目前程序的運作時間配額,其值逐漸減小。一旦減至0,就要從可執行隊列runqueue中目前的位置移到末尾,宏操 作NICE_TO_TICKS根據系統時鐘的精度将程序的優先級别換算成可以運作的時間配額,即恢複其初始的時間配額。把該程序移到末尾意味着:如果沒有 權值更高的程序,但是有一個權值與這相同的程序存在,那麼,那個權值相同而排列在前的程序就會被選中,進而顧全了大局。

接下來排程函數查詢目前運作程序的狀态是否改變:

Move_rr_back:

switch(prev-》state){ Case TASK_INTERRUPTIBLE:

if (signal pending(prev)){ Prev-》state=TASK_RUNNING;

Break;

}

default: Del_from_runqueue (prev);

Case TASK_RUNNING:

}

Prev-》need_resched=0;

容易了解:如果發現程序處于TASK_INTERRUPTIBLE狀态且有信号等待處理,則核心将其狀态設為TASK_RUNNING,讓其處理完信号, 接下來仍有機會獲得CPU;如果沒有信号等待,則将其從可運作隊列中撤下來;如果處于TASK_RUNNING狀态,則繼續進行。然後,将 prev-》need_resched的值恢複成0,因為所需的排程已經在運作。

Repeat schedule ():

next=idle_task(this_cpu); c=-1000;  If (prev-》state==TASK_RUNNING)

Goto still_running;

Still_running_back:

List_for_each (tmp, &runqueue_head){

P=list_entry (tmp, struct task_struct, run_list);if (can_schedule(p,this_cpu)){ Int weight=goodness (p, this_cpu, prev-》active_mm);if (weight》c)

C=weight, next=p;

}

}

排程之前,将待排程的程序預設為0号程序,權值置為-1000。0号程序比較特别,既不會睡眠,又不能被殺死。接下來核心周遊可執行隊列run queue中的每個程序,為每個程序通過goodness()函數計算出它目前所具有的權值,然後與目前的最高值c相比。如果兩個程序具有相同權值的話, 那麼排在前面的程序勝出。

Still_running:

C=goodness (prev, this_cpu, prev-》active_mm);Next=prev;

Goto still_running_back;

上面的代碼告訴我們,如果目前程序想要繼續運作,那麼在挑選程序時以目前程序此刻的權值開始。這意味着,相對于權值相同的其他程序來說,目前程序優先。

若發現目前已選程序的權值為0,則需要重新計算各個程序的時間配額,schedule()将轉入recalculate部分。限于篇幅,在此不再展開。

4.結束語

以上結合代碼簡要介紹了Linux中程序排程的基本思想、依據和政策,容易發現Linux高效率和較強支援并發程序等特點。近年來,嵌入式Linux的研 究正在成為一個熱點,了解Linux程序排程的原理,并在此基礎上改進排程算法可能存在的缺陷,可以進一步增強其對實時性的支援,使之進一步适應在嵌入式 系統領域内的應用。