一、綜述
圖1是塊設備操作的一個分層實現圖。當一個進程調用read讀取一個文件時,內核執行如下一個過程:首先,它通過VFS層去讀取要到的文件塊有沒有已經被cache了,這個cache由一個buffer_head結構讀取。如果要讀取的文件塊還沒有被cache,則就要從文件系統中去讀取了,這就是文件系統的映射層,它通過一個address_space結構來引用,然後調用文件系統讀函數(readpage)去讀取一個頁面大小的數據,這個讀函數對於不同的文件系統來說,是不一樣的。當它從磁盤中讀出數據時,它會將數據頁鏈入cache中,當下次再讀取時,就不需要再次從磁盤出去讀了。Readpage()函數並不是直接去操作磁盤,而只是將請求初始化成一個bio結構,並提交給通用塊層(generic block layer)。
圖1
它就通過submit_bio()去完成的。通用塊層再調用相應設備的IO調度器,通過這個調度器的調度算法,將這個bio或合併到已存在的request中,或創建一個新的request,並將這個新創建的request插入到設備的請求隊列中去。這就完成了IO調度層的工作。最後就是塊設備驅動所做的工作了。IO調度器傳遞給塊驅動的是一個請求隊列,塊驅動就是要處理這個隊列中的請求,直到這個隊列爲空爲止。
二、通用塊層(generic block layer)
通用塊層操作的是一個bio結構,這個結構主要的數據域是,
unsigned short bi_vcnt;
struct bio_vec *bi_io_vec; /* the actual vec list */
這個就是要讀寫的數據向量,且每個struct bio_vec 爲一個segment。
//這個函數主要是調用generic_make_request()去完成工作:
void submit_bio(int rw, struct bio *bio)
{
……
generic_make_request(bio);
}
//這個函數的主要作用是將bio傳遞給驅動去處理
void generic_make_request(struct bio *bio)
{
……
do {
char b[BDEVNAME_SIZE];
//取得塊設備相應的隊列,每個設備一個
q = bdev_get_queue(bio->bi_bdev);
/*
* If this device has partitions, remap block n
* of partition p to block n+start(p) of the disk.
*/
blk_partition_remap(bio); //塊設備分區信息轉換,如將相對於一個分區的的偏移地址轉換成相對於整個塊設備的絕對偏移等等。
old_sector = bio->bi_sector;
old_dev = bio->bi_bdev->bd_dev;
……
//這個是塊設備隊列的請求處理函數。由塊設備創建請求隊列時初始化。
//對於IDE等設備,它是__make_request()。但對於ramdisk就不一樣了。
ret = q->make_request_fn(q, bio); // __make_request()等
} while (ret);
}
//這要函數的主要作用就是調用IO調度算法將bio合併,或插入到隊列中合適的位置中去
static int __make_request(request_queue_t *q, struct bio *bio)
{
struct request *req;
int el_ret, nr_sectors, barrier, err;
const unsigned short prio = bio_prio(bio);
const int sync = bio_sync(bio);
int rw_flags;
nr_sectors = bio_sectors(bio);
//用於處理高端內存
blk_queue_bounce(q, &bio);
spin_lock_irq(q->queue_lock);
//測試是否能合併,本文忽略IO調度算法
el_ret = elv_merge(q, &req, bio);
switch (el_ret) {
//前兩種可以合併
case ELEVATOR_BACK_MERGE:
……
goto out;
case ELEVATOR_FRONT_MERGE:
……
goto out;
//不能合併,需要新創一個request。
/* ELV_NO_MERGE: elevator says don't/can't merge. */
default:
;
}
get_rq:
rw_flags = bio_data_dir(bio);
if (sync)
rw_flags |= REQ_RW_SYNC;
//新創一個request
req = get_request_wait(q, rw_flags, bio);
//初始化這個request。
init_request_from_bio(req, bio);
spin_lock_irq(q->queue_lock);
if (elv_queue_empty(q)) //空隊列的處理
blk_plug_device(q);
add_request(q, req); //將新請求加入隊列中去
out:
if (sync) //如果需要同步,立即處理請求
__generic_unplug_device(q);
spin_unlock_irq(q->queue_lock);
return 0;
end_io:
bio_endio(bio, nr_sectors << 9, err);
return 0;
}
//觸發塊設備驅動進行真正的IO操作
void __generic_unplug_device(request_queue_t *q)
{
if (unlikely(blk_queue_stopped(q)))
return;
if (!blk_remove_plug(q))
return;
q->request_fn(q); //設備的請求處理函數,屬於驅動層
}