Linux塊設(shè)備中的IO路徑及調(diào)度策略
當(dāng)文件系統(tǒng)通過(guò)submit_bio提交IO之后,請(qǐng)求就進(jìn)入了通用塊層。通用塊層會(huì)對(duì)IO進(jìn)行一些預(yù)處理的動(dòng)作,其目的是為了保證請(qǐng)求能夠更加合理的發(fā)送到底層的磁盤設(shè)備,盡量保證性能***。這里面比較重要的就是IO調(diào)度模塊。大家可能都聽說(shuō)過(guò)CFQ,除此之前還有DeadLine和Noop等,這些都是磁盤的調(diào)度算法。其中CFQ調(diào)度算法用的最多。
如果忽略塊設(shè)備的層疊結(jié)構(gòu)和各種映射,簡(jiǎn)化的結(jié)構(gòu)大概有3層,如圖1所示。這里的3層并非都是軟件,還包含硬件。通用塊層就不用多說(shuō)了,這里主要完成IO的合并和調(diào)度等操作。其下是驅(qū)動(dòng)層,驅(qū)動(dòng)層是硬件的驅(qū)動(dòng)程序,用于將IO請(qǐng)求轉(zhuǎn)換為對(duì)硬件寄存器的操作(注:不同的塊設(shè)備又有差異,必然iSCSI設(shè)備是不會(huì)有寄存器操作的)。物理設(shè)備不同該驅(qū)動(dòng)層的程序就不同,比如對(duì)于SAS直連的磁盤,該驅(qū)動(dòng)層的程序就是SAS驅(qū)動(dòng),而如果是FC-HBA卡連接的FC-SAN,那么這個(gè)驅(qū)動(dòng)層就是FC驅(qū)動(dòng)(比如Qlogic的驅(qū)動(dòng))。
圖1 塊設(shè)備分層
最下面一層是設(shè)備層,設(shè)備層通常是一個(gè)硬件設(shè)備。這里的硬件種類繁多,比如SAS卡、SATA卡、FC-HBA卡或者iSCSI-HBA卡等等。但有的時(shí)候又可能并不是硬件設(shè)備,比如對(duì)于iSCSI來(lái)說(shuō),該層可能是通過(guò)軟件模擬的一個(gè)設(shè)備層,而其請(qǐng)求則是通過(guò)網(wǎng)卡發(fā)送到目標(biāo)器端。
主要數(shù)據(jù)結(jié)構(gòu)及流程
絕大多數(shù)程序都是由數(shù)據(jù)結(jié)構(gòu)和算法2部分內(nèi)容組成的,數(shù)據(jù)結(jié)構(gòu)相當(dāng)于程序的骨架,而算法則是程序的筋和肉。通過(guò)算法將數(shù)據(jù)結(jié)構(gòu)關(guān)聯(lián)起來(lái),從而形成一個(gè)完整的整體。人類認(rèn)識(shí)問(wèn)題的規(guī)律是從具體到抽象,從簡(jiǎn)單到復(fù)雜,因此我們先從數(shù)據(jù)結(jié)構(gòu)開始。理解了數(shù)據(jù)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),那我們就能更加容易的理解塊設(shè)備IO的整個(gè)邏輯。
在塊設(shè)備IO中最為關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)是request_queue,也就是請(qǐng)求隊(duì)列。該數(shù)據(jù)結(jié)構(gòu)的簡(jiǎn)圖如圖2所示,這個(gè)數(shù)據(jù)結(jié)構(gòu)本身非常復(fù)雜,我們這里進(jìn)行了簡(jiǎn)化,只保留了部分關(guān)鍵的成員。如圖彩色部分是2個(gè)函數(shù)指針,分別用于接收請(qǐng)求和處理請(qǐng)求。
圖2 請(qǐng)求隊(duì)列數(shù)據(jù)結(jié)構(gòu)
為了便于理解,我們這里舉一個(gè)例子。以NBD塊設(shè)備為例,在塊設(shè)備初始化的時(shí)候make_request_fn被初始化為blk_queue_bio,request_fn被初始化為do_nbd_request。對(duì)于SCSI塊設(shè)備而言,request_fn會(huì)被初始化為scsi_request_fn。
有了上面數(shù)據(jù)結(jié)構(gòu)的知識(shí)及關(guān)鍵成員初始化的結(jié)果,接下來(lái)我們就可以分析一下塊設(shè)備的整個(gè)流程的細(xì)節(jié)。塊設(shè)備請(qǐng)求的入口是submit_bio,經(jīng)過(guò)簡(jiǎn)單的檢查后調(diào)用
由上述代碼可以看出IO處理的入口函數(shù)其實(shí)是函數(shù)指針make_request_fn,而我們知道該指針實(shí)際上是函數(shù)blk_queue_bio。因此塊設(shè)備的請(qǐng)求會(huì)由blk_queue_bio函數(shù)進(jìn)行處理。
磁盤調(diào)度策略
Linux內(nèi)核在設(shè)計(jì)磁盤的調(diào)度策略時(shí)提供了極大的靈活性。磁盤的調(diào)度策略以插件的注冊(cè)到內(nèi)核當(dāng)中,也就是用戶可以自由的選擇磁盤的調(diào)度策略。
調(diào)度算法的思想其實(shí)非常簡(jiǎn)單,主要是通過(guò)對(duì)IO的排序、合并和批量處理來(lái)優(yōu)化磁盤尋道和請(qǐng)求的處理時(shí)間。這里值得說(shuō)明的目前的調(diào)度算法其實(shí)更多的是針對(duì)機(jī)械磁盤,因?yàn)闄C(jī)械磁盤磁頭定位耗時(shí)占整個(gè)IO處理時(shí)間的很大比例。當(dāng)然對(duì)于SSD磁盤,調(diào)度算法也有一定的幫助,這就需要針對(duì)IO的特性具體來(lái)看了。
圖3 調(diào)度策略結(jié)構(gòu)體
磁盤調(diào)度策略的結(jié)構(gòu)體定義如圖3所示,各個(gè)變量的含義也是比較明確,本文不再贅述。本文主要看一下 其中elevator_ops類型的變量ops,這個(gè)變量是調(diào)度策略具體的功能實(shí)現(xiàn),任何調(diào)度算法都要實(shí)現(xiàn)其中某些函數(shù)。
調(diào)度策略的實(shí)現(xiàn)就是通過(guò)這些回調(diào)函數(shù)完成的。為了理解調(diào)度策略的函數(shù)集具體做哪些事情,本文整理了一個(gè)表格,我們先從整體上看一下每個(gè)函數(shù)具體做了哪些事情。對(duì)于調(diào)度策略來(lái)說(shuō),這里的函數(shù)并非每個(gè)都要實(shí)現(xiàn),下表中只有帶*的才是必須要實(shí)現(xiàn)的函數(shù)。
簡(jiǎn)而言之,上述回調(diào)函數(shù)的功能就是判斷請(qǐng)求是否可以被合并、執(zhí)行合并和請(qǐng)求下發(fā)等等操作。上述回調(diào)函數(shù)比較多,而且使用場(chǎng)景也比較復(fù)雜,具體使用分散在調(diào)度器的很多流程中。因此,我們很難一下子介紹清楚所有的場(chǎng)景。為了更加直觀的理解上述回調(diào)函數(shù)的作用,我們以Deadline調(diào)度策略為例進(jìn)行簡(jiǎn)單的介紹。
如圖4是Deadline初始化的回調(diào)函數(shù),從圖中可以看出這里并沒(méi)有初始化所有的回調(diào)函數(shù),而只初始化了16個(gè)回調(diào)函數(shù)中的9個(gè)。
圖4 Deadline回調(diào)函數(shù)
我們具體分析一下函數(shù)的調(diào)用場(chǎng)景,前文我們介紹到elevator_merge_fn函數(shù)用于查詢可以與bio合并的請(qǐng)求。如圖5所示為整個(gè)調(diào)用棧,入口為blk_queue_bio,這個(gè)函數(shù)我們之前介紹過(guò),它就是調(diào)度程序的入口。該函數(shù)調(diào)用elv_merge用于查找是否有可以合并的請(qǐng)求,并返回。而elv_merge函數(shù)調(diào)用的正式Deadline調(diào)度器提供的回調(diào)函數(shù)。完成判斷后,該函數(shù)會(huì)根據(jù)實(shí)際情況返回請(qǐng)求(或者沒(méi)有找到,不返回)和可合并的方向(例如向前合并,向后合并等),后續(xù)流程就是進(jìn)行具體的合并操作了。
圖5 函數(shù)調(diào)用棧
由于IO調(diào)度涉及的流程比較多,限于本文篇幅,今天就先介紹到這里。后續(xù)我們?cè)俑由钊氲慕榻B關(guān)于IO調(diào)度的其它內(nèi)容。