從使用者空間開始進行對檔案的讀過程
讀:
讀取一個檔案的時候,陷入系統呼叫,先檢查資料是否在快取中,如果沒有則觸發一次讀盤操作,然後等待磁碟上的資料被更新到快取中。
讀取磁碟過程:呼叫檔案系統層的readpages函式,使用各種檔案系統層的get_block函式獲取磁碟實體地址,存放到bh裡(即buffer_head),使用bh構造bio,然後提交bio(一般使用submit_bio函式將資料bio提交到io的塊裝置層)。函式generic_make_request轉發bio,generic_make_request是一個迴圈,通過generic_make_request提交請求給I/O排程層,這個函式最後呼叫到q->make_request_fn(q, bio),那麼對於這個函式的呼叫就是I/O排程層的入口點(ps: Generic_make_request的執行上下文可能有兩種,一種是使用者上下文,另一種為pdflush.)
q->make_request_fn呼叫的是blk_queue_bio函式(早期版本是__make_request),在blk_init_allocated_queue裡註冊。blk_queue_bio函式是Linux提供的塊裝置請求處理函式,實現IO Schedule。在該函式中試圖將轉發過來的bio merge到一個已經存在的request中,如果可以合併,那麼將新的bio請求掛載到一個已經存在request中。如果不能合併,那麼分配一個新的request,然後將bio新增到其中。
blk_queue_bio裡分為plug和unplug機制 ,在plug下就直接把request存到plug list(例如dio就是依賴於plug機制)。unplug下就直接呼叫queue的request_fn方法把request提交給磁碟驅動進行真正的處理了。當然,我們現在使用的是unplug機制。
然後q->request_fn呼叫queue佇列的request_fn方法scsi_request_fn函式(我們這裡選擇sda,sdb之類scsi裝置),
在scsi_request_fn函式中會掃描request佇列,通過blk_peek_request(原先版本是:elv_next_request)函式從佇列中獲取一個request。在blk_peek_request函式中通過scsi匯流排層註冊的q->prep_rq_fn(scsi層註冊為scsi_prep_fn)函式將具體的request轉換成scsi驅動所能認識的scsi command。獲取一個request之後,scsi_request_fn函式直接呼叫scsi_dispatch_cmd函式將scsi command傳送給一個具體的scsi host。
到這一步, 在scsi_dispatch_cmd函式中,通過scsi host的介面方法queuecommand將scsi command傳送給scsi host。通常scsi host的queuecommand方法會將接收到的scsi command掛到自己維護的佇列中,然後再啟動DMA過程將scsi command中的資料傳送給具體的磁碟。DMA完畢之後,DMA控制器中斷CPU,告訴CPU DMA過程結束,並且在中斷上下文中設定DMA結束的中斷下半部。DMA中斷服務程式返回之後觸發軟中斷,執行SCSI中斷下半部。
在SCSi中斷下半部中,呼叫scsi command結束的回撥函式,這個函式往往為scsi_done,在scsi_done函式呼叫blk_complete_request函式結束請求request,每個請求維護了一個bio鏈,所以在結束請求過程中回撥每個請求中的bio回撥函式,結束具體的bio。Bio又有檔案系統的buffer head生成,所以在結束bio時,回撥buffer_head的回撥處理函式bio->bi_end_io(註冊為end_bio_bh_io_sync)。自此,由中斷引發的一系列回撥過程結束,總結一下回調過程如下:scsi_done->end_request->end_bio->end_bufferhead。
那什麼時候知道資料已經在快取裡了呢?
do_generic_file_read –> PageUptodate(page) 即檢查 PG_uptodate標誌位。
參考的資料:
Linux scsi裝置讀寫流程 :
http://blog.chinaunix.net/uid-29634482-id-5127267.html
塊裝置讀寫流程 :
http://blog.chinaunix.net/uid-25052030-id-58337.html
usb驅動學習筆記 :
http://blog.chinaunix.net/uid-25627207-id-3341609.html
plug/unplug機制 :
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=14528823&id=4778396