IO的生命周期
●?將來自cache的數據封裝成bio
submit_bh->submit_bh_wbc
此時IO還在fs層
●?進入block IO層
submit_bh_wbc->submit_io-> generic_make_request
上面獲得的q是每個設備(block_device)的隊列。block_device有一個成員queue,所有針對該設備的請求都會放入其中,該queue不是后面將要說到的三類queue。
make_queue_fn的注冊在request queue初始化的時候:blk_init_queue->blk_init_allocated_queue
●?開始將bio合并到request:
generic_make_request->blk_queue_bio
blk_queue_bio實現了對bio的合并調度。它調用的函數elv_merge是關鍵函數,它尋找可以用來合并bio的request。
IO從塊設備層(block IO layer),到發送到塊設備驅動(device driver)整個過程經過三類隊列:
?????? 1)unplug request queue 屬于線程
?????? 2)elevator queue 調度隊列,不同的調度器,隊列不同
?????? 3)device request queue 派遣隊列,dispatch queue。(例如,在deadline_dispatch_requests中實現)
?
plug和unplug:目的是讓請求不馬上被驅動程序處理。設備處于pluged狀態,設備不會被激活。處于unplugged狀態,被激活。
?
來自上層的請求,先嘗試合并入unplug 隊列,若不能合并,則調用elv_merge合并入調度隊列elevator queue。若找不到可合并的請求,則獲得一個空請求request,用該bio初始化該request,然后放到unplug隊列中。
此時bio(request)還在unplug 隊列中。
此時bio(request)在調度隊列中。
●?(若不能放入unplug隊列)調用elv_merge,找到可以合并bio的request。
blk_queue_bio->elv_merge
elv_merge的作用主要是,找到可以將bio合并的request。
這一步,是調用特定的調度器找到可以合并bio的request
?
●?將unplug 隊列中的request放到調度隊列
blk_queue_bio:
通過blk_flush_plug_list(也可通過_elv_add_request)將unplug 請求隊列中的請求發送到調度隊列elevator queue。
add_acct_request->__elv_add_request
此時request在調度隊列中。
●?將調度隊列里的request發送到派遣隊列:
返回blk_queue_bio,然后blk_queue_bio-> add_acct_request-> __elv_add_request
?
此時,request在device request queue(派遣隊列)中。
這一步實現具體IO調度器對請求的派遣(發送到派遣隊列):
__elv_add_request->elv_drain_elevator->elevator_dispatch_fn
elevator_dispatch_fn將被注冊為具體調度器的派遣函數,例如deadline_dispatch_request
IO調度器的工作:合并,排序。
排序:使請求按扇區增長的方向有序排列。
CFQ:每個發起IO的進程都有一個隊列。
Deadline:有4個隊列,分為兩類sort_list和fifo_list。每類都有讀寫兩種隊列。
?????? sort_list 按請求起始扇區排序,fifo_list按請求生成的時間排序。
?
此時,請求在派遣隊列(device request queue)中。
●?進入驅動程序,并獲得一個request:
返回blk_queue_bio: blk_queue_bio-> __blk_run_queue-> __blk_run_queue_uncond->request_fn(scsi_request_fn)-> blk_peek_request->__elv_next_request
request_fn被注冊為scsi_request_fn,該函數是驅動程序的入口。
?
從device request queue中獲得一個請求,準備發送到scsi塊設備驅動(中間層)
●?將request轉化成scsi command:
在blk_peek_request中調用q->prep_rq_fn(注冊為scsi_prep_fn),將request轉化成scsi驅動能夠識別scsi command。
●?發送scsi command到scsi host:
回到scsi_request_fn,調用scsi_dispatch_cmd將scsi command發送給scsi host
●?DMA:
在scsi_dispatch_cmd中,調用queuecommand方法,將scsi command掛在自己的隊列中,然后啟動DMA,將scsi command發送到具體的磁盤。DMA完畢后,DMA控制器中斷CPU,告訴CPU DMA結束。并且在中斷上下文中,設置DMA結束的中斷下半部。DMA中斷處理程序返回之后,觸發軟中斷,執行scsi中斷下部。
驅動:scsi中間層(middle level driver) + ?scsi host driver。
scsi中間層抽象了scsi總線邏輯;scsi host driver控制scsi總線控制器,實現scsi數據的物理層傳輸。
queuecommand是這兩層之間的橋梁。它將被注冊為具體的物理塊設備的函數,例如megaraid_queue。
●?執行中斷下半部分,并返回:
在scsi中斷下部,調用scsi command結束的回調函數scsi_done:scsi_dispatch_cmd->scsi_done。scsi_done調用blk_complete_request結束請求。
●
?
轉載于:https://www.cnblogs.com/volcanorao/p/5977618.html
總結
- 上一篇: 我的spark学习之路(三):利用spa
- 下一篇: java创建文件和目录