2.6.11-進程調度算法之一

1.運行隊列

進程調度的基本數據結構運行隊列,數據結構是struct runqueue定義在<kernel/sched.c>。
struct runqueue變量,是定義成PER_CPU類型的,一個CPU的核心對應一個運行隊列。每個進程在同一時刻只能處於一個運行隊列裏。

static DEFINE_PER_CPU(struct runqueue, runqueues);

有時候必須對運行隊列中的信息進行更改,因此必須對其進行加鎖,確保在同一時間不會有不同的修改操作,已造成不可預知的後果。當前CPU的核企圖給其他CPU核的運行隊列上鎖的情況偶爾也會發生,這在以後會看到。最通常的給運行隊列上鎖的技術是task_rq_lock()和task_rq_unlock()函數。
struct runqueue結構的定義如下:

 198 struct runqueue {                                                               
 199         spinlock_t lock;                                                        
 200                                                                                 
 201         /*                                                                                                                                                                                                   
 202          * nr_running and cpu_load should be in the same cacheline because      
 203          * remote CPUs use both these fields when doing load calculation.       
 204          */                                                                     
 205         unsigned long nr_running;                                               
 206 #ifdef CONFIG_SMP                                                               
 207         unsigned long cpu_load;                                                 
 208 #endif                                                                          
 209         unsigned long long nr_switches;                                         
 210                                                                                 
 211         /*                                                                      
 212          * This is part of a global counter where only the total sum            
 213          * over all CPUs matters. A task can increase this counter on           
 214          * one CPU and if it got migrated afterwards it may decrease            
 215          * it on another CPU. Always updated under the runqueue lock:           
 216          */                                                                     
 217         unsigned long nr_uninterruptible;                                       
 218                                                                                 
 219         unsigned long expired_timestamp;                                        
 220         unsigned long long timestamp_last_tick;                                 
 221         task_t *curr, *idle;                                                    
 222         struct mm_struct *prev_mm;                                              
 223         prio_array_t *active, *expired, arrays[2];                              
 224         int best_expired_prio;                                                  
 225         atomic_t nr_iowait; 
}

2. 優先隊列

在runqueue結構的定義中,有這麼兩個prio_array_t結構指針,分別是活動隊列(active)和過氣隊列(expired)。
定義如下:

  1. 活動隊列:所有時間片還剩餘的進程的集合。
  2. 過氣隊列:那些已耗盡時間片的進程的集合。
    這兩個隊列的定義如下:
prio_array_t *active, *expired, arrays[2];
 390 typedef struct prio_array prio_array_t;
 185 struct prio_array {                                                             
 186         unsigned int nr_active;                                                 
 187         unsigned long bitmap[BITMAP_SIZE];                                                                                                                                                                   
 188         struct list_head queue[MAX_PRIO];                                       
 189 };

nr_active是當前優先隊列中可運行的進程數。MAX_PROI的值爲140,用於表示系統中的每個優先級(因爲兩個Linux使用的優先級映射後的值正好是[0,140])。BITMAP_SIZE的值是5,因爲我們用bitmap中的每一位來對應一個系統優先級,共140個優先級所以需要140位來表示。考慮到unsigned long通常爲32位長,所以需要5個這樣的數據才足夠140位(實際上爲5 * 32 = 160 位)。到這裏我們能看出明顯的對應關係,bitmap和queue的容量剛好都能對應於每個優先級。這樣的對應關係的作用是什麼?
初始化時,bitmap中的每一位都置位0,當某進程變可運行的時候(也就是說它的狀態變成TASK_RUNNING),其優先級對應於bitmap中的位置1。這樣就簡化了搜尋的工作量——要找到當前可運行的最高優先級的進程,只需要找到bitmap中第一個爲1的位。因爲優先級數目是固定的,所以搜尋工作的時間複雜度不會受到當前進程數目的影響。

在enqueue_task函數中,會設置活動隊列和過氣隊列的中成員。

 574 static void enqueue_task(struct task_struct *p, prio_array_t *array)            
 575 {                                                                               
 576         sched_info_queued(p);                                                                                                                                               
 577         list_add_tail(&p->run_list, array->queue + p->prio);                    
 578         __set_bit(p->prio, array->bitmap);                                      
 579         array->nr_active++;                                                     
 580         p->array = array;                                                       
 581 }    

主要做了如下動作:
1)在576行, 通過sched_info_queued函數設置進程的時間片。
2)在577行,把struct task中的run_list加入到prio_array_t活動隊列或者過期隊列的queue中。
3)在578行,設置bitmap中對應的bit,比如第2個隊列,則bitmap中第2個bit。
4)在579行,給nr_active加1,因爲加入了1個task到隊列中
5)在580行,把struct task中的array指向當前rq中的array指向active或者expired。

問題:
1)在prio_array結構中,queue[0]中,是不是隻掛一個進程?
2)在enqueue_task函數中,在578行調用__set_bit(p->prio, array->bitmap);沒看到清bitmap位,在進程運行完成後,應該要清除bitmap位。回頭再跟代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章