每個塊設備或者塊設備的分區,都對應有自身的請求隊列(request_queue),而每個請求隊列都可以選擇一個I/O調度器來協調所遞交的request。I/O調度器的基本目的是將請求按照它們對應在塊設備上的扇區號進行排列,以減少磁頭的移動,提高效率。在前面討論遞交I/O請求的時候可以發現,每個request_queue都有一個request的隊列,隊列裡的請求將按順序被響應。實際上,除了這個隊列,每個調度器自身都維護有不同數量的隊列,用來對遞交上來的request進行處理,而排在隊列最前面的request將適時被移動到request_queue中等待響應。內核中實現的IO調度器主要有四種--Noop,Deadline,CFG以及最復雜的as.我們不妨從最簡單的noop開始研究,順便看一下調度器是如何與request_queue聯系上的。
首先要了解描述elevator的數據結構。和elevator相關的數據結構有個,一個是elevator_type,一個是elevator_queue,前者對應一個調度器類型,後者對應一個調度器實例,也就說如果內核中只有上述四種類型的調度器,則只有四個elevator_type,但是多個塊設備(分區)可擁有多個相應分配器的實例,也就是elevator_queue。兩個數據結構中最關鍵的元素都是struct elevator_ops,該結構定義了一組操作函數,用來描述請求隊列的相關算法,實現對請求的處理。
struct elevator_type
{
struct list_head list;
struct elevator_ops ops;
struct elv_fs_entry *elevator_attrs;
char elevator_name[ELV_NAME_MAX];
struct module *elevator_owner;
};
struct elevator_queue
{
struct elevator_ops *ops;
void *elevator_data;
struct kobject kobj;
struct elevator_type *elevator_type;
struct mutex sysfs_lock;
struct hlist_head *hash;
};
函數elevator_init()用來為請求隊列分配一個I/O調度器的實例
int elevator_init(struct request_queue *q, char *name)
{
struct elevator_type *e = NULL;
struct elevator_queue *eq;
int ret = 0;
void *data;
/*初始化請求隊列的相關元素*/
INIT_LIST_HEAD(&q->queue_head);
q->last_merge = NULL;
q->end_sector = 0;
q->boundary_rq = NULL;
/*下面根據情況在elevator全局鏈表中來尋找適合的調度器分配給請求隊列*/
if (name) {//如果指定了name,則尋找與name匹配的調度器
e = elevator_get(name);
if (!e)
return -EINVAL;
}
//如果沒有指定io調度器,並且chosen_elevator存在,則尋找其指定的調度器
if (!e && *chosen_elevator) {
e = elevator_get(chosen_elevator);
if (!e)
printk(KERN_ERR "I/O scheduler %s not found\n",
chosen_elevator);
}
//依然沒獲取到調度器的話則使用默認配置的調度器
if (!e) {
e = elevator_get(CONFIG_DEFAULT_IOSCHED);
if (!e) {//獲取失敗則使用最簡單的noop調度器
printk(KERN_ERR
"Default I/O scheduler not found. " \
"Using noop.\n");
e = elevator_get("noop");
}
}
//分配並初始化elevator_queue
eq = elevator_alloc(q, e);
if (!eq)
return -ENOMEM;
//調用ops中的elevator_init_fn函數,針對調度器的隊列進行初始化
data = elevator_init_queue(q, eq);
if (!data) {
kobject_put(&eq->kobj);
return -ENOMEM;
}
//建立數據結構的關系
elevator_attach(q, eq, data);
return ret;
}