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)。
定義如下:
- 活動隊列:所有時間片還剩餘的進程的集合。
- 過氣隊列:那些已耗盡時間片的進程的集合。
這兩個隊列的定義如下:
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位。回頭再跟代碼。