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