linux核心分析筆記—-調度

來源:互聯網
上載者:User

       調度?咋這熟悉,我們是不是常在哪裡聽到。沒錯,是的,調度我們時常聽過,比如交通管制調度啦等。這不,夏天這熱, 標語貼的好:相應國電電力調度,做文明市民,好彆扭啊!不管了。你要是還是不懂,再囉嗦講個事,過年回家,和漂亮的GF回家,為了張普通的硬座票還要排老久對,甚至還可能被坑拿到黃牛票,這時你嘴裡咧咧的啥:XX,啥火車站,做的啥春運調度啊!唉,這次你說到點上了。

       總結一下:調度就是通過發送器的合理調度,實現系統資源的最大限度發揮作用。多進程的並發正是這樣的效果。其實原理一點也不複雜,道理也一樣簡單:只要又可以執行的進程,那麼就總是會有進程正在執行。但簡單的問題也會複雜化,比如:我們買票為啥抱怨調度,歸根接地感謝當年的人海戰術(多說一句,其實現實的很多問題,一個人海戰術解決所有,這戰術中國人用起來最得心應手)。好麼,一般系統中進程的數目總會比處理器的個數多,所以競爭在所難免,調度的問題就集中在解決競爭的問題。

       種類問題不多說:搶佔和非搶佔。linux提供了搶佔式的多任務模式,下一階段誰得到CPU的執行時間由發送器決定。這怎麼決定,是不是請個客,喝個酒啥的。對不起,linux無情的說,我是開源的,對所有人公平,哥不吃這一套。我有自己的一套原則(策略,這個我們待會兒再講)。接著來術語,上面的過程叫做搶佔,進程得到CPU的執行機會這段時間叫時間片,是由系統定義好的。後面我們會看到,linux發送器採用動態方法計算時間片。說了搶佔,再說說非搶佔,就是說沒人跟你搶,除非自己放棄,否則你自己運行下去吧。進程主動放棄掛起自己的行為叫讓步。這種機制看似很和諧(不要被和諧哦),但問題挺多,比如系統不知道每個進程執行多長時間。而且一旦一個懸掛進程不願意或者忘了做出讓步,那系統崩了咋辦,我的money,我的future,我的lover,從此一去不複返。

       說了那多,開始正題。原則,一切都有原則,linux的原則。開始原則以前,再來認識一下我們前邊說過的進程,其實進程遠不如以前那樣簡單。進程也有種類的,至少分為IO型和CPU型。IO型指那種大部分時間用來提交I/O請求或是等待I/O請求的進程。這樣的進程挺討人厭的,他通常都是運行一小小會兒,因為它總是在等待更多的I/O請求時最後總會阻塞。與之對應的則是CPU型進程。這樣的進程把時間大多放在執行代碼上,除非被搶佔,他們就一直貪得無厭的運行。因為它們沒有太多的IO請求。由於它們不是IO驅動類型,也就不存在使用者互動的問題(IO驅動不僅僅指磁碟,還指鍵盤滑鼠等),所有從系統互動響應速度而言,調度器不應該經常讓他們運行。即降低它們的運行頻率,而將它們的已耗用時間拖長一些。這裡所說的原則就是:調度策略要在進程響應迅速(相應時間短)和進程輸送量高之間做出艱難的決定。這個原則有時不公平,很殘酷。殘酷的讓人不懂,不懂低優先順序的有時不能公平對待。由於互動式進程屬於IO型進程,所以linux為了保證互動式應用,所以調度策略會向IO型進行傾斜。

        上面提到優先順序,調度演算法中最基本的一類當然就是基於優先順序的調度。挺簡單:優先順序高的先運行,相同優先順序的按輪轉式進行調度。優先順序高的進程使用的時間片也長。發送器總是選擇時間片未用盡且優先順序最高的進程運行。這句話就是說使用者和系統可以通過設定進程的優先順序來響應系統的調度。基於此,linux設計上一套動態優先順序的調度方法。一開始,先為進程設定一個基本的優先順序,然而它允許發送器根據需要來加減優先順序。linux核心提供了兩組獨立的優先順序範圍。第一種是nice值,範圍從-20到19,預設為0.nice值越大優先順序越小。另外nice值也用來決定分配給進程時間片的長短。第二種範圍是即時優先順序。預設範圍是從0到99.任何即時的優先順序都高於普通優先順序。這個我們後面再說。

       說了那麼多的時間片,說起來不費勁,做起來那不是一個麻煩可以說清楚的。時間片就是一個數值,它表明進程在被搶佔前所能持續啟動並執行時間。預設的時間片是20ms,很短。linux發送器還能根據進程的優先順序動態調整分配給他的時間片。特別說明的是,進程不一定要一次用完所有分配給它的時間片。一旦進程的時間片耗盡後,就認為進程到期了。沒有時間片的進程不會再投入運行。除非等到其他的進程都耗盡了它們的時間片,這時,所有進程的時間片就會被重新計算。

       前邊講到了搶佔的話題,當一個進程進入TASK_RUNNING狀態時,核心就會檢測它的優先順序是否高於當前正在執行的進程。如果是,則發送器會被喚醒,重新選擇新的進程執行(套用書上的話, 應該是剛剛進入可運行狀態的這個進程)。此外,當一個進程的時間片變為0時,它會被搶佔,發送器被喚醒以選擇一個新的進程。(具體的例子大家可以看看參考書籍的p28頁3.1.5)

       linux的發送器定義於kernel/sched.c中,在2.6核心中的發送器和以前有了很大的差別,體現在下面:

1.充分實現O(1)調度.這個應該懂得,可以懂的
2.在多核處理器的今天,linux也不落伍,全面實現SMP的擴充性。每個處理器擁有自己的鎖和自己的可執行隊列
3.強化SMP的親和力。這是有關多核CPU親和力的說明,我目前正在研究這個,後面我會專門在一篇博文中詳細說明。
4.加強互動能力。
5.保證公平。
6.雖然最常見的最佳化情況是系統中只有1~2個可運行進程,但是最佳化也完全有能力擴充到具有多處理器且每個處理器上運行多個進程的系統中。

      上面第二條可執行隊列,它是發送器中最基本的資料結構,定義於kernel/sched.c中,由結構runquene表示。它是給定處理器上的可執行進程的鏈表,每個處理器維護一個。每個可投入運行得進程都唯一的歸屬於一個可執行隊列。此外,可執行隊列中還包含每個處理器的調度資訊。具體的結構這裡就不給了,自己要有看原始碼的能力哦。宏cpu_rq(processor)用於返回給定處理器可執行隊列的指標,this_rq()用於來返回當前處理器的可執行隊列,task_rq(task)返回給定任務所在的隊列指標。

       在其擁有者讀取或改寫其隊列成員的時候,可執行隊列包含的鎖用來防止隊列被其他代碼改動,由於每個可執行隊列唯一地對應一個處理器,所以很少出現一個處理器需要鎖其他處理器的可執行隊列的情況,但這種情況是事實存在的。最常見的情況是發生在一個特定的任務在運行隊列上執行時。此時需要用到task_rq_lock()和task_rq_unlock()函數,或者可以用this_rq_lock()來鎖住當前的可執行隊列,用rq_unlock(struct runqueue *rq)釋放給定隊列上的鎖。關於鎖的操作,我們再次飄過,為啥?一方面,這超出了本節的重點,二者我在linux驅動理論帖中對加解鎖做了詳細說明,三者,我後面可能還會詳細說。所以,那句話真是太真理了:暫時的放下,是再次的拿起。

        從運行隊列的結構中,我們發現了兩個有關優先順序的數組(定義在kernel/sched.c中,名字叫prio_array,具體自己去看吧):活躍的和過去的。這兩個數組是實現O(1)級演算法的資料結構。優先順序數組使可運行處理器的每一種優先順序都包含一個相應的隊列,而這些隊列包含對應優先順序上的可執行進程鏈表。結構中的優先順序位元影像主要是為了協助提高尋找當前系統內擁有最高優先順序的可執行進程的效率而設計的。關於結構中的定義常量,MAX_PRIO定義了系統擁有的優先順序個數,預設值是140,BITMAP_SIZE是優先順序位元影像數組的大小,值是5,這個是可以計算出來的。比如:位元影像數組的類型是unsigned long類型,長是32位,假定每一位代表一個優先順序(4*32 = 128<140),就需要5個這樣的長整型才能表示。所以,bitmap就正好含有5個數組項。

        開始時,位元影像成員的所有位都被清0,當某一優先順序的進程開始準備執行時,位元影像中對應的位就置1,這樣,尋找系統中最高的優先順序就變成了尋找位元影像中被設定的第一位。鑒於優先順序個數的定值性,尋找的時間就不受系統到底有多少可執行進程的影響,是個定值。關於對位元影像的快速尋找演算法對應的函數是sched_find_first_bit().

        優先順序數組的list_head隊列是一個鏈表,這個鏈表包含了該處理器上相應優先順序的全部可運行線程。要找到下一個要啟動並執行任務,直接從該鏈表中選擇下一個元素。對於給定的優先順序,按輪轉方式調度任務。優先順序數組的計數器nr_active,它儲存了該優先順序數組內可執行進程的數目。

         下一話題,我前邊反覆提到時間片的話題,一旦所有進程的時間片都用完會怎樣了,總不能系統宕掉吧,好在作業系統提供了一種顯式的方法來重新計算每個進程的時間片。典型就是逐一查看每個進程。借用書上的一個例子:

for (系統中的每個任務){
           重新計算優先順序
           重新計算時間片

       這種方法簡單,但弊端很多:

1.可能耗費相當長的時間。
2.為了保護任務隊列和每個進程的描述符,必要要用到鎖的機制。這樣做很明顯會加劇對鎖的爭用,影響系統效能。
3.重算時間片的時機不確定。
4.實現顯得很粗糙。

       現在採用的是新的調度方法,每個處理器擁有上面提到的兩個優先順序數組,活動數組上的進程都有時間片剩餘,而到期數組中的進程都耗盡了時間片,當一個進程的時間片耗盡時,它會移至到期數組,但在此之前,時間片已經給它重新計算好了,重新計算時間片現在變的非常簡單,只要在活動和到期數組之間來回切換就可以了。又因為數組是通過指標進行訪問的,所以交換它們用的時間就是交換指標需要的時間。這個動作由schedule()完成:

struct prio_array *array = rq->active;
if(!array->nr_active){
        rq->active = rq->expired;
        rq->expired = array;
}

       這種交換是O(1)級發送器的核心。O(1)級發送器根本不需要從頭到尾都忙著重新計算時間片,它只要完成一個兩個步驟就能實現數組的切換,這種實現完美地解決了前面列舉的所有弊端。linux的發送器是在schedule()函數實現的。當核心代碼想要休眠時,會直接調用該函數,如果哪個進程將被搶佔,也會被喚起執行。調度的第一步是判斷誰是優先順序最高的進程:

struct task_struct *prev, *next;
struct list_head *queue;
struct prio_array array;
int idx;
prev = current;
array = rq->active;
idx = sched_find_first_bit(array->bitmpa);
queue = array->queue + idx;
next = list_entry(queue->next, struct task_struct, run_list);

       分析上面這段代碼,首先在活動優先順序數組中找到第一個被設定的bit位,該位對應著最高的可執行進程。然後,發送器選擇這個層級鏈表裡的頭一個進程。這個進程就是系統中優先順序最高的可執行程式,也是馬上就會被調度執行的進程。上面中的prev和next不等,說明被選中的進程不是當前進程,這時就要調用context_switch來進程切換。最後強調一點,不存在任何影響schedule()時間長短的因素,因為前邊說過,所用的時間是恒定的。

       前邊多次說過:發送器是利用優先順序和時間片來做出決定的。下邊看看實際代碼的實現方式。進程結構體告訴我們進程擁有一個初始的優先順序,叫做nice值,最低是19,最高是20,結構體的static_prio域就存放這個值,static就是說這個一開始就由使用者指定好了,不能改變。發送器要用的動態優先順序用prio域表示,兩者的關係在於通過一個關於靜態優先順序和進程互動性的Function Compute而來。啥叫進程互動性?這是個新詞,第一次提到,這樣說吧,我們前邊說過I/O消耗性和CPU消耗性的進程區別,進程互動性就是指IO消耗性的,因為和使用者進行互動就是電腦IO和使用者互動,像滑鼠鍵盤等。但我們怎麼確定一個進程的互動性的強弱呢?最明顯的標準莫過於通過計算進程休眠的時間長短來做預測了。大部分時間都在休眠的進程當然是IO消耗型的,如果一個進程把所有的時間幾乎都放在了CPU執行處理上,那..不說了。在linux中,用值tast_struct中的sleep_avg域記錄了一個進程用於休眠和用於執行的時間,範圍為0~MAX_SLEEP_AVG,預設值是10ms。當一個進程從休眠狀態恢複到執行狀態時,sleep_avg會根據它休眠時間的長短而增長直到最大。相反,進程沒運行一個時鐘節拍,sleep_avg就做相應的遞減,到0為止。這種方法不僅會獎勵互動性強的進程,還會懲罰處理器耗時量的進程。再加之獎勵和處罰都是建立在作為基數的nice值上,所有使用者仍然能夠通過改變nice指來調整程式的調度。effective_prio()函數可以返回一個進程的動態優先數,該函數以nice值為基數,再加上我們上面提到的從-5到+5的進程互動性型獎勵處罰分。

       一旦有了上面的動態優先順序,再來重新計算時間片就方便多了。另外,當一個進程建立的時候,建立的子進程和父進程均分父進程剩餘的時間片。這樣的分配很平均並且防止使用者通過不斷的建立新進程來不斷攫取時間片。task_timeslice()函數為給定任務返回一個新的時間片,時間片的計算只需要把優先順序按比例縮放,使其符合時間片的數值範圍要求就可以了。優先順序最高的進程能獲得的最大時間片長度(MAX_TIMESLICE)是200ms,最短時間片(MIN_TIMESLICE)是10ms。預設優先順序(nice值為0,沒有互動性獎罰分)的進程得到的時間片長度為100ms。上面我們也提到過,活動數組內的可執行隊列上的進程的時間片耗盡時,它就會移植到到期數組。但是,如果一個進程的互動性很強,特別特彆強,那麼發送器支援另外一種機制來支援這種互動進程:當時間片用完後,它會被再放置到活動數組而不是到期數組中。回憶一下上面O(1)調度器的實現機制:進程用盡其時間片後,都會被移動到到期數組,當活動數組中沒有剩餘進程的時候,這個時候兩個數組就會被交換。但是在發生這種交換以前,互動性很強的一個進程有可能已經處於到期數組中了,當他需要互動的時候,這時可能數組互動還沒有發生,所以互動也就無法進行,這時對於這種互動特別特彆強的進程重新插入到活動數組就可以避免這種問題。注意,雖然被放回到活動數組,但不會立即就運行,它還要和其他具有相同優先順序的進程輪流程執行。該邏輯在scheduler_tick()中實現,該函數會被定時器中斷調用。看下面的實現代碼:

struct  task_struct *task = current;
struct runqueue *rq = this_rq();
if(!—task->time_slice){
       if(!TASK_INTERACTIVE(task) || (EXPIRED_STARVING(rq)))
                 enqueue_task(task, rq->expired);
       else
                 enqueue_task(task, rq->active);
}

       這段代碼先減少進程時間片的值。宏TASK_INTERACTIVE主要是基於進程的nice值來查看這個進程是否是互動性很強的進程。宏EXPIRED_STARVING負責檢查到期數組內的進程是否處於饑餓狀態,是否有相當長的時間沒有發生數組交換了。如果最近一直沒有發生切換,那麼再把當前的進程放置到活動數組會進一步拖延切換--到期數組內的進程會來越來越餓.只要不發生這種情況,進程就會被重新設定在活動數組裡,否則,進程會被放入到期數組裡。

      有關休眠我就不用多講了。核心對休眠的處理都相同:進程把自已標幟成休眠狀態,然後將自己從可執行隊列移出,放入等待隊列,然後調用schedule()選擇和執行一個其他進程,喚醒的過程剛好相反。要明白一點就是:即使休眠也有兩種進程狀態,TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE.前者如果接收到一個訊號會被提前喚醒並響應該訊號,後者會忽略訊號。這兩種進程位於同一等待隊列(wake_queue_head_t)上,等待某些事情,不能夠運行。有關等待隊列的知識,跳過(我已經在Linux驅動開發理論帖裡講過,自己去看吧)現在,在核心中進行休眠的推薦操作相比較以前而言複雜了很多,但更安全:

1.調用DECLARE_WAITQUEUE() 建立一個等待隊列的項。
2.調用add_wait_queue()把自己加入到等帶對列中。
3.將進程的狀態變更為TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE.
4.檢查條件是否為真,如果是的話,就沒必要休眠了。不為真,就調用schedule().
5.當進程被喚醒的時候,它會再次檢查條件是否為真。如果是,它就退出迴圈,如果不是,它再次調用schedule()並一直重複這步操作。
6.當條件滿足後,進程將自己設定為TASK_RUNNING並調用remove_wait_queue把自己移出等待隊列。

       如果在進程開始休眠之前條件就已經達成了,那麼迴圈會退出,進程不會存在錯誤的進入休眠的傾向。有休眠就有喚醒,喚醒是通過wake_up()進行。她喚醒指定的等待隊列上的所有進程。它調用try_to_wake_up,該函數負責將進程設定為TASK_RUNNING狀態,調用active_task()將此進程放入可執行隊列,如果被喚醒進程的優先順序比當前正在執行的進程的優先順序高,還要設定need_resched標誌。編程的技巧,通常哪段代碼促使等待條件達成,它就要負責隨後調用wake_up函數。說明:存在虛假的喚醒,有時候進程被喚醒並不是因為它所等待的條件達成了,所以才需要用一個迴圈處理來保證它等待的條件真正到達。

       開始新的問題,現在市場上,你說單核CPU,你都沒臉見人,現在是啥時代,多核的時代,瞧,我這都酷睿i7了(四核),我用的伺服器實驗平台是8核心的。和我有啥關係,和我今天講的有啥關係。關係大了去了。前邊提到過,linux的發送器為對稱式多處理器系統的每個處理器準備了單獨的可執行隊列和鎖,出於效率的考慮,整個調度系統從每個處理器來看都是獨立的。那麼這樣的情況咋辦呢?一個處理器上有5個進程,另外一個處理器的隊列上只有一個進程,這時很明顯出現了系統負載不均衡的情況。這時咋辦呢?由負載平衡程式來解決(kernel/sched.c的load_balance來實現)。它有兩種調用方法,在schedule執行的時候,只要當前的可執行對列為空白,它就會被調用。此外,它也會被定時器調用,系統空閑時每隔1ms調用一次或者在其他情況下每隔200ms調用一次。單系統中,它不會被調用,甚至不會被編譯進核心,因為那裡邊只有一個可執行隊列,根本沒有平衡負載的必要。load_balances調用時需要鎖住當前處理器的可執行隊列並且屏蔽中斷,以避免可執行隊列被並發的訪問,所完成的操作步驟如下所示:

1.load_balance()調用find_busiest_queue(),找到最繁忙的可執行隊列(進程數目最多)並返回該隊列。如果沒有哪個可執行隊列中進程的數
  目比當前隊列中的數目多25%或25%以上。它就返回NULL,同時load_balance也返回。
2.load_balance從最繁忙的運行隊列中選擇一個優先順序數組以便抽取進程。最好是到期數組,如果到期數組為空白,那就只能選活動數組。
3.load_balance尋找到含有進程並且優先順序最高的鏈表,因為把優先順序高的進程分散開來才是最重要的。
4.分析找到的所有這些優先順序相同的進程,這樣一個不是正在執行,也不會因為處理器相關性而不可移動,並且不在快取中的進程。如
  果有,就調用pull_task將其從最繁忙的隊列中抽取到當前隊列
5.只要可執行隊列之間仍然不平衡,就重複上面兩個步驟,這最終會消除不平衡。然後解除對當前運行隊列進行的鎖定,從load_balance返
  回。

我們後面會提到的環境切換,就是從一個可執行進程切換到另一個可執行進程,由定義在kernel/sched.c的context_switch函數負責處理。每當一個新的進程被選出來準備投入啟動並執行時候,schedule就會調用該函數。它主要完成如下兩個工作:

1.調用定義在include/asm/mmu_context.h中的switch_mm().該函數負責把虛擬記憶體從上一個進程映射切換到新進程中。
2.調用定義在include/asm/system.h的switch_to(),該函數負責從上一個進程的處理器狀態切換到新進程的處理器狀態,這包括儲存,恢複
   棧資訊和寄存器資訊。

      核心也必須知道什麼時候調用schedule(),單靠使用者代碼顯示調用schedule(),他們可能就會永遠地執行下去,相反,核心提供了一個need_resched標誌來表明是否需要重新執行一次調度。當某個進程耗盡它的時間片時,scheduler_tick()就會設定這個標誌,當一個優先順序高的進程進入可執行狀態的時候,try_to_wake_up()也會設定這個標誌。下表給出了用於訪問和操作need_resched的函數。

函數

說明

set_tsk_need_resched(task) 設定指定進程中的need_resched標誌
clear_tsk_need_resched(task) 清除指定進程中的nedd_resched標誌
need_resched() 檢查need_resched標誌的值,如果被設定就返回真,否則返回假

       再返回使用者空間以及從中斷返回的時候,核心也會檢查need_resched標誌,如果已被設定,核心會在繼續執行之前調用該發送器。最後,每個進程都包含一個need_resched標誌,這是因為訪問進程描述符內的數值要比訪問一個全域變數要快(因為current宏速度很快並且描述符通常都在快取中)。在2.6核心中,他被移到了thread_info結構體裡,這是和以前不同的。

       搶佔,還是搶佔!我們總是逃不了搶佔的話題。核心放回到使用者空間的時候,如果need_resched標誌被設定,就會導致schedule()被調用。簡兒言之,使用者搶佔在以下情況下發生:從系統調用返回到使用者空間和從中斷處理常式返回使用者空間。

       在linux2.6核心中,核心引入了搶佔內力。為了支援核心搶佔所做的第一個變動就是為每個進程的thread_info引入preempt_count計數器,該計數器初始為0,每當使用鎖的時候,該值減1。當為0的時候,核心就可執行搶佔。從中斷返回核心空間的時候,核心會檢查need_resched和preempt_count的值,如果need_resched被設定,並且preempt_count為0的時候,這說明有一個更重要的任務需要執行並且安全地搶佔,此時,發送器就會被調度。如果,preempt_count不為0,說明當前任務持有鎖,這時搶佔是不安全的。這時,就會想通常那樣直接從中斷返回當前執行進程。如果當前進程持有的鎖都被釋放了,那麼preempt_count就會重新為0。此時,釋放鎖的代碼會檢查need_sheduled是否被設定,如果是的話,就會調用發送器,有些核心需要允許或禁止核心搶佔,這個後面具體問題具體分析。如果核心中的進程阻塞了,或它顯示地調用了schedule(),核心搶佔也會顯示地發生,這種形式的核心搶佔從來都是支援的,因為根本無需額外的邏輯來保證核心可以安全地被搶佔,如果代碼顯示的調用了schedule(),那麼它清楚自己是可以安全地被強化的。

       我們前邊講的是普通的,非即時的調度策略(SCHED_OTHER),Linux也支援兩種即時調度策略(SCHED_FIFO和SCHED_RR).SCHED_FIFO從名字中我們也知道,就不說了。它不使用時間片。該級的進程會比任何SCHED_OTHER級的進程都先得到調度。一旦一個SCHED_FIFO級進程處理可執行狀態,就會一直執行,直到它自己被阻塞或是顯示地釋放處理器為止。由於不基於時間片,所以它會一直執行下去。多個SCHED_FIFO級進程存在時,他們會輪流執行。而且只要有SCHED_FIFO級的進程存在,普通級進程級只能等待它結束後才有機會執行。SCHED_RR是一種帶有時間片的SCHED_FIFO。以上兩種即時調度演算法實現都是靜態優先順序的。核心不為即時進程計算動態優先順序。這樣就能保證給定優先順序層級的即時進程總能搶佔優先順序比它低的進程。

       linux的即時調度演算法分為軟即時和硬即時。軟即時的含義是,核心調度進程儘可能使進程在它的限定時間到來前執行,但核心不會拍著胸脯說:我一定能保證。對應地,硬即時會這樣哥們義氣哦。linux下的即時調度不做任何保證,只保證只要即時進程可執行,就盡量去執行它。現在開來,效果還是不錯的。

       即時優先順序範圍從0~MAX_RT_PRIO-1.預設情況下,MAX_RT_PRIO為100.SCHED_OTHER級進程的nice值共用了這個取值空間。它的取值範圍是從MAX_RT_PRIO到MAX_RT_PRIO+40.也就是說,nice值從-20到+19對應的是從100到140的即時優先順序範圍。

       最後,是一些關於和調度有關的系統調用,這個我就不說了,感覺就是函數調用,說了沒啥意思,自己看吧。反正就是一書在手,萬事不愁啊..呵呵

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.