色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

IO請(qǐng)求在block layer的來(lái)龍去脈

Linux閱碼場(chǎng) ? 來(lái)源:未知 ? 作者:胡薇 ? 2018-06-06 15:39 ? 次閱讀

所謂請(qǐng)求合并就是將進(jìn)程內(nèi)或者進(jìn)程間產(chǎn)生的在物理地址上連續(xù)的多個(gè)IO請(qǐng)求合并成單個(gè)IO請(qǐng)求一并處理,從而提升IO請(qǐng)求的處理效率。在前面有關(guān)通用塊層介紹的系列文章當(dāng)中我們或多或少地提及了IO請(qǐng)求合并的概念,本篇我們從頭集中梳理IO請(qǐng)求在block layer的來(lái)龍去脈,以此來(lái)增強(qiáng)對(duì)IO請(qǐng)求合并的理解。首先來(lái)看一張圖,下面的圖展示了IO請(qǐng)求數(shù)據(jù)由用戶進(jìn)程產(chǎn)生,到最終持久化存儲(chǔ)到物理存儲(chǔ)介質(zhì),其間在內(nèi)核空間所經(jīng)歷的數(shù)據(jù)流以及IO請(qǐng)求合并可能的觸發(fā)點(diǎn)。

從內(nèi)核的角度而言,進(jìn)程產(chǎn)生的IO路徑主要有圖中①②③所示的三條:

①緩存IO, 對(duì)應(yīng)圖中的路徑①,系統(tǒng)中絕大部分IO走的這種形式,充分利用filesystem 層的page cache所帶來(lái)的優(yōu)勢(shì), 應(yīng)用程序產(chǎn)生的IO經(jīng)系統(tǒng)調(diào)用落入page cache之后便可以直接返回,page cache中的緩存數(shù)據(jù)由內(nèi)核回寫(xiě)線程在適當(dāng)時(shí)機(jī)負(fù)責(zé)同步到底層的存儲(chǔ)介質(zhì)之上,當(dāng)然應(yīng)用程序也可以主動(dòng)發(fā)起回寫(xiě)過(guò)程(如fsync系統(tǒng)調(diào)用)來(lái)確保數(shù)據(jù)盡快同步到存儲(chǔ)介質(zhì)上,從而避免系統(tǒng)崩潰或者掉電帶來(lái)的數(shù)據(jù)不一致性。緩存IO可以帶來(lái)很多好處,首先應(yīng)用程序?qū)O丟給page cache之后就直接返回了,避免了每次IO都將整個(gè)IO協(xié)議棧走一遍,從而減少了IO的延遲。其次,page cache中的緩存最后以頁(yè)或塊為單位進(jìn)行回寫(xiě),并非應(yīng)用程序向page cache中提交了幾次IO, 回寫(xiě)的時(shí)候就需要往通用塊層提交幾次IO, 這樣在提交時(shí)間上不連續(xù)但在空間上連續(xù)的小塊IO請(qǐng)求就可以合并到同一個(gè)緩存頁(yè)中一并處理。再次,如果應(yīng)用程序之前產(chǎn)生的IO已經(jīng)在page cache中,后續(xù)又產(chǎn)生了相同的IO,那么只需要將后到的IO覆蓋page cache中的舊IO,這樣一來(lái)如果應(yīng)用程序頻繁的操作文件的同一個(gè)位置,我們只需要向底層存儲(chǔ)設(shè)備提交最后一次IO就可以了。最后,應(yīng)用程序?qū)懭氲絧age cache中的緩存數(shù)據(jù)可以為后續(xù)的讀操作服務(wù),讀取數(shù)據(jù)的時(shí)候先搜索page cache,如果命中了則直接返回,如果沒(méi)命中則從底層讀取并保存到page cache中,下次再讀的時(shí)候便可以從page cache中命中。

②非緩存IO(帶蓄流),對(duì)應(yīng)圖中的路徑②,這種IO繞過(guò)文件系統(tǒng)層的cache。用戶在打開(kāi)要讀寫(xiě)的文件的時(shí)候需要加上“O_DIRECT”標(biāo)志,意為直接IO,不讓文件系統(tǒng)的page cache介入。從用戶角度而言,應(yīng)用程序能直接控制的IO形式除了上面提到的“緩存IO”,剩下的IO都走的這種形式,就算文件打開(kāi)時(shí)加上了 ”O(jiān)_SYNC” 標(biāo)志,最終產(chǎn)生的IO也會(huì)進(jìn)入蓄流鏈表(圖中的Plug List)。如果應(yīng)用程序在用戶空間自己做了緩存,那么就可以使用這種IO方式,常見(jiàn)的如數(shù)據(jù)庫(kù)應(yīng)用。

③非緩存IO(不帶蓄流),對(duì)應(yīng)圖中的路徑③,內(nèi)核通用塊層的蓄流機(jī)制只給內(nèi)核空間提供了接口來(lái)控制IO請(qǐng)求是否蓄流,用戶空間進(jìn)程沒(méi)有辦法控制提交的IO請(qǐng)求進(jìn)入通用塊層的時(shí)候是否蓄流。嚴(yán)格的說(shuō)用戶空間直接產(chǎn)生的IO都會(huì)走蓄流路徑,哪怕是IO的時(shí)候附上了“O_DIRECT” 和 ”O(jiān)_SYNC”標(biāo)志(可以參考《Linux通用塊層介紹(part1: bio層)》中的蓄流章節(jié)),用戶間接產(chǎn)生的IO,如文件系統(tǒng)日志數(shù)據(jù)、元數(shù)據(jù),有的不會(huì)走蓄流路徑而是直接進(jìn)入調(diào)度隊(duì)列盡快得到調(diào)度。注意一點(diǎn),通用塊層的蓄流只提供機(jī)制和接口而不提供策略,至于需不需要蓄流、何時(shí)蓄流完全由內(nèi)核中的IO派發(fā)者決定。

應(yīng)用程序不管使用圖中哪條IO路徑,內(nèi)核都會(huì)想方設(shè)法對(duì)IO進(jìn)行合并。內(nèi)核為促進(jìn)這種合并,在IO協(xié)議棧上設(shè)置了三個(gè)最佳狙擊點(diǎn):

lCache (頁(yè)高速緩存)

lPlug List (蓄流鏈表)

lElevator Queue (調(diào)度隊(duì)列)

cache 合并

IO處在文件系統(tǒng)層的page cache中時(shí)只有IO數(shù)據(jù),還沒(méi)有IO請(qǐng)求(bio 或 request),只有page cache 在讀寫(xiě)的時(shí)候才會(huì)產(chǎn)生IO請(qǐng)求。本文主要介紹IO請(qǐng)求在通用塊層的合并,因此對(duì)于IO 在cache 層的合并只做現(xiàn)象分析,不深入到內(nèi)部邏輯和代碼細(xì)節(jié)。如果是緩存IO,用戶進(jìn)程提交的寫(xiě)數(shù)據(jù)會(huì)積聚在page cache 中。cache 保存IO數(shù)據(jù)的基本單位為page,大小一般為4K, 因此cache 又叫“頁(yè)高速緩存”, 用戶進(jìn)程提交的小塊數(shù)據(jù)可以緩存到cache中的同一個(gè)page中,最后回寫(xiě)線程將一個(gè)page中的數(shù)據(jù)一次性提交給通用塊層處理。以dd程序?qū)懸粋€(gè)裸設(shè)備為例,每次寫(xiě)1K數(shù)據(jù),連續(xù)寫(xiě)16次:

dd if=/dev/zero of=/dev/sdb bs=1k count=16

通過(guò)blktrace觀測(cè)的結(jié)果為:

blktrace -d /dev/sdb -o - | blkparse -i -

bio請(qǐng)求在通用塊層的處理情況主要是通過(guò)第六列反映出來(lái)的,如果對(duì)blkparse的輸出不太了解,可以 man 一下blktrace。對(duì)照每一行的輸出來(lái)看看應(yīng)用程序產(chǎn)生的寫(xiě)IO經(jīng)由page cache之后是如何派發(fā)到通用塊層的:

現(xiàn)階段只關(guān)注IO是如何從page cache中派發(fā)到通用塊層的,所以后面的瀉流、派發(fā)過(guò)程沒(méi)有貼出來(lái)。回寫(xiě)線程–kworker以8個(gè)扇區(qū)(扇區(qū)大小為512B, 8個(gè)扇區(qū)為4K對(duì)應(yīng)一個(gè)page大小)為單位將dd程序讀寫(xiě)的1K數(shù)據(jù)塊派發(fā)給通用塊層處理。dd程序?qū)懥?6次,回寫(xiě)線程只寫(xiě)了4次(對(duì)應(yīng)四次Q),page cache的緩存功能有效的合并了應(yīng)用程序直接產(chǎn)生的IO數(shù)據(jù)。文件系統(tǒng)層的page cache對(duì)讀IO也有一定的作用,帶緩存的讀IO會(huì)觸發(fā)文件系統(tǒng)層的預(yù)讀機(jī)制,所謂預(yù)讀有專門的預(yù)讀算法,通過(guò)判斷用戶進(jìn)程IO趨勢(shì),提前將存儲(chǔ)介質(zhì)上的數(shù)據(jù)塊讀入page cache中,下次讀操作來(lái)時(shí)可以直接從page cache中命中,而不需要每次都發(fā)起對(duì)塊設(shè)備的讀請(qǐng)求。還是以dd程序讀一個(gè)裸設(shè)備為例,每次讀1K數(shù)據(jù),連續(xù)讀16次:

dd if=/dev/sdb of=/dev/zero bs=1K count=16

通過(guò)blktrace觀測(cè)的結(jié)果為:

blktrace -d /dev/sdb -o - | blkparse -i -

同樣只關(guān)注IO是如何從上層派發(fā)到通用塊層的,不關(guān)注IO在通用塊層的具體情況,先不考慮P,I,U,D,C等操作,那么上面的輸出可以簡(jiǎn)單解析為:

讀操作是同步的,所以觸發(fā)讀請(qǐng)求的是dd進(jìn)程本身。dd進(jìn)程發(fā)起了16次讀操作,總共讀取16K數(shù)據(jù),但是預(yù)讀機(jī)制只向底層發(fā)送了兩次讀請(qǐng)求,分別為0+32(16K), 32+64(32K),總共預(yù)讀了16 + 32 = 48K數(shù)據(jù),并保存到cache中,多預(yù)讀的數(shù)據(jù)可以為后續(xù)的讀操作服務(wù)。

plug 合并

在閱讀本節(jié)之前可以先回顧下linuxer公眾號(hào)中介紹bio和request的系列文章,熟悉IO請(qǐng)求在通用塊層的處理,以及蓄流(plug)機(jī)制的原理和接口。特別推薦宋寶華老師寫(xiě)的《文件讀寫(xiě)(BIO)波瀾壯闊的一生》,通俗易懂地介紹了一個(gè)文件io的生命周期。

每個(gè)進(jìn)程都有一個(gè)私有的蓄流鏈表,進(jìn)程在往通用塊層派發(fā)IO之前如果開(kāi)啟了蓄流功能,那么IO請(qǐng)求在被發(fā)送給IO調(diào)度器之前都保存在蓄流鏈表中,直到泄流(unplug)的時(shí)候才批量交給調(diào)度器。蓄流的主要目的就是為了增加請(qǐng)求合并的機(jī)會(huì),bio在進(jìn)入蓄流鏈表之前會(huì)嘗試與蓄流鏈表中保存的request進(jìn)行合并,使用的接口為blk_attempt_plug_merge(). 本文是基于內(nèi)核4.17分析的,源碼來(lái)源于4.17-rc1。

代碼遍歷蓄流鏈表中的request,使用blk_try_merge找到一個(gè)能與bio合并的request并判斷合并類型,蓄流鏈表中的合并類型有三種:ELEVATOR_BACK_MERGE,ELEVATOR_FRONT_MERGE,ELEVATOR_DISCARD_MERGE。普通文件IO操作只會(huì)進(jìn)行前兩種合并,第三種是丟棄操作的合并,不是普通的IO的合并,故不討論。

bio后向合并 (ELEVATOR_BACK_MERGE)

為了驗(yàn)證IO請(qǐng)求在通用塊層的各種合并形式,準(zhǔn)備了以下測(cè)試程序,該測(cè)試程序使用內(nèi)核原生支持的異步IO引擎,可異步地向內(nèi)核一次提交多個(gè)IO請(qǐng)求。為了減少page cache和文件系統(tǒng)的干擾,使用O_DIRECT的方式直接向裸設(shè)備派發(fā)IO。

iotc.c

...

/* dispatch 3 4k-size ios using the io_type specified by user */

#define NUM_EVENTS 3

#define ALIGN_SIZE 4096

#define WR_SIZE 4096

enum io_type {

SEQUENCE_IO,/* dispatch 3 ios: 0-4k(0+8), 4-8k(8+8), 8-12k(16+8) */

REVERSE_IO,/* dispatch 3 ios: 8-12k(16+8), 4-8k(8+8),0-4k(0+8) */

INTERLEAVE_IO, /* dispatch 3 ios: 8-12k(16+8), 0-4k(0+8),4-8k(8+8) */ ,

IO_TYPE_END

};

int io_units[IO_TYPE_END][NUM_EVENTS] = {

{0, 1, 2},/* corresponding to SEQUENCE_IO */

{2, 1, 0},/* corresponding to REVERSE_IO */

{2, 0, 1}/* corresponding to INTERLEAVE_IO */

};

char *io_opt = "srid:";/* acceptable options */

int main(int argc, char *argv[])

{

int fd;

io_context_t ctx;

struct timespec tms;

struct io_event events[NUM_EVENTS];

struct iocb iocbs[NUM_EVENTS],

*iocbp[NUM_EVENTS];

int i, io_flag = -1;;

void *buf;

bool hit = false;

char *dev = NULL, opt;

/* io_flag and dev got set according the options passedby user , don’t paste the code of parsing here to shrink space */

fd = open(dev, O_RDWR | __O_DIRECT);

/* we can dispatch 32 IOs at 1 systemcall */

ctx = 0;

io_setup(32, &ctx);

posix_memalign(&buf,ALIGN_SIZE,WR_SIZE);

/* prepare IO request according to io_type */

for (i = 0; i < NUM_EVENTS; iocbp[i] = iocbs + i, ++i)

io_prep_pwrite(&iocbs[i], fd, buf, WR_SIZE,io_units[io_flag][i] * WR_SIZE);

/* submit IOs using io_submit systemcall */

io_submit(ctx, NUM_EVENTS, iocbp);

/* get the IO result with a timeout of 1S*/

tms.tv_sec = 1;

tms.tv_nsec = 0;

io_getevents(ctx, 1, NUM_EVENTS, events, &tms);

return 0;

}

測(cè)試程序接收兩個(gè)參數(shù),第一個(gè)為作用的設(shè)備,第二個(gè)為IO類型,定義了三種IO類型:SEQUENCE_IO(順序),REVERSE_IO(逆序),INTERLEAVE_IO(交替)分別用來(lái)驗(yàn)證蓄流階段的bio后向合并、前向合并和泄流階段的request合并。為了減少篇幅,此處貼出的源碼刪除了選項(xiàng)解析和容錯(cuò)處理,只保留主干,原版位于:https://github.com/liuzhengyuan/iotc。

為驗(yàn)證bio在蓄流階段的后向合并,用上面的測(cè)試程序iotc順序派發(fā)三個(gè)寫(xiě)io:

# ./iotc-d/dev/sdb-s

-d 指定作用的設(shè)備sdb, -s 指定IO方式為SEQUENCE_IO(順序),表示順序發(fā)起三個(gè)寫(xiě)請(qǐng)求: bio0(0 + 8), bio1(8 + 8), bio2(16 + 8)。通過(guò)blktrace來(lái)觀察iotc派發(fā)的bio請(qǐng)求在通用塊層蓄流鏈表中的合并情況:

blktrace -d /dev/sdb -o - | blkparse -i -

上面的輸出可以簡(jiǎn)單解析為:

第一個(gè)bio(bio0)進(jìn)入通用塊層時(shí),此時(shí)蓄流鏈表為空,于是申請(qǐng)一個(gè)request并用bio0初始化,再將request添加進(jìn)蓄流鏈表,同時(shí)告訴blktrace蓄流已正式工作。第二個(gè)bio(bio1)到來(lái)的時(shí)候會(huì)走blk_attempt_plug_merge的邏輯,嘗試調(diào)用bio_attempt_back_merge與蓄流鏈表中的request合并,發(fā)現(xiàn)正好能合并到第一個(gè)bio所在的request尾部,于是直接返回。第三個(gè)bio(bio2)的處理與第二個(gè)同理。通過(guò)蓄流合并之后,三個(gè)IO請(qǐng)求最終合并成了一個(gè)request(0 + 24)。用一副圖來(lái)展示整個(gè)合并過(guò)程:

bio前向合并 (ELEVATOR_FRONT_MERGE)

為驗(yàn)證bio在蓄流階段的前向合并,使用iotc逆序派發(fā)三個(gè)寫(xiě)io:

# ./iotc-d/dev/sdb-r

-r 指定IO方式為REVERSE_IO(逆序),表示逆序發(fā)起三個(gè)寫(xiě)請(qǐng)求: bio0(16 + 8),bio1(8 + 8), bio2(0 + 8)。blktrace的觀察結(jié)果為:

blktrace -d /dev/sdb -o - | blkparse -i -

上面的輸出可以簡(jiǎn)單解析為:

與前面的后向合并相比,唯一的區(qū)別是合并方式由之前的”M”變成了現(xiàn)在的”F”,即在blk_attempt_plug_merge中走的是bio_attempt_front_merge分支。通過(guò)下面的圖來(lái)展示前向合并過(guò)程:

“plug 合并”不會(huì)做request與request的進(jìn)階合并,蓄流鏈表中的request之間的合并會(huì)在泄流的時(shí)候做,即在下面介紹的“elevator 合并”中做。

elevator 合并

上面講到的蓄流鏈表合并是為進(jìn)程內(nèi)的IO請(qǐng)求服務(wù)的,每個(gè)進(jìn)程只往自己的蓄流鏈表中提交IO請(qǐng)求,進(jìn)程間的蓄流鏈表相互獨(dú)立,互不干涉。但是,多個(gè)進(jìn)程可以同時(shí)對(duì)一個(gè)設(shè)備發(fā)起IO請(qǐng)求,那么通用塊層還需要提供一個(gè)節(jié)點(diǎn),讓進(jìn)程間的IO請(qǐng)求有機(jī)會(huì)進(jìn)行合并。一個(gè)塊設(shè)備有且僅有一個(gè)請(qǐng)求隊(duì)列(調(diào)度隊(duì)列),所有對(duì)塊設(shè)備的IO請(qǐng)求都需要經(jīng)過(guò)這個(gè)公共節(jié)點(diǎn),因此調(diào)度隊(duì)列(Elevator Queue)是IO請(qǐng)求合并的另一個(gè)節(jié)點(diǎn)。先回顧一下通用塊層處理IO請(qǐng)求的核心函數(shù):blk_queue_bio(), 上層派發(fā)的bio請(qǐng)求都會(huì)流經(jīng)該函數(shù),或?qū)io蓄流到Plug List,或?qū)io合并到Elevator Queue, 或?qū)io生成request直接插入到Elevator Queue。blk_queue_bio()的主要處理流程為:

其中”A”標(biāo)識(shí)的“合并到蓄流鏈表的request中”就是上一章介紹的“plug 合并”。bio如果不能合并到蓄流鏈表中接下來(lái)會(huì)嘗試合并到“B”標(biāo)識(shí)的”合并到調(diào)度隊(duì)列的request中”。”合并到調(diào)度隊(duì)列的request中”只是“elevator 合并”的第一個(gè)點(diǎn)。你可能已經(jīng)發(fā)現(xiàn)了blk_queue_bio()將bio合并到蓄流鏈表或者將request添加進(jìn)蓄流鏈表之后就沒(méi)管了,從路徑①可以發(fā)現(xiàn)蓄流鏈表中的request最終都是要交給電梯調(diào)度隊(duì)列的,這正是”elevator 合并”的第二個(gè)點(diǎn),關(guān)于泄流的時(shí)機(jī)請(qǐng)參考我之前寫(xiě)的《Linux通用塊層介紹(part1: bio層)》。下面分別介紹這兩個(gè)合并點(diǎn):

bio合并到elevator

先看B表示的代碼段:

blk_queue_bio:

switch (elv_merge(q, &req, bio)) {

case ELEVATOR_BACK_MERGE:

if (!bio_attempt_back_merge(q, req, bio))

break;

elv_bio_merged(q, req, bio);

free = attempt_back_merge(q, req);

if (free)

__blk_put_request(q, free);

else

elv_merged_request(q, req, ELEVATOR_BACK_MERGE);

goto out_unlock;

case ELEVATOR_FRONT_MERGE:

if (!bio_attempt_front_merge(q, req, bio))

break;

elv_bio_merged(q, req, bio);

free = attempt_front_merge(q, req);

if (free)

__blk_put_request(q, free);

else

elv_merged_request(q, req, ELEVATOR_FRONT_MERGE);

goto out_unlock;

default:

break;

}

合并邏輯基本與”plug 合并”相似,先調(diào)用elv_merge接口判斷合并類型,然后根據(jù)是后向合并或是前向合并分別調(diào)用bio_attempt_back_merge和bio_attempt_front_merge進(jìn)行合并操作,由于操作對(duì)象從蓄流鏈表變成了電梯調(diào)度隊(duì)列,bio合并完了之后還需額外干幾件事:

1.調(diào)用elv_bio_merged, 該函數(shù)會(huì)調(diào)用電梯調(diào)度器注冊(cè)的elevator_bio_merged_fn接口來(lái)通知調(diào)度器做相應(yīng)的處理,對(duì)于deadline調(diào)度器而言該接口為NULL。

2.尋找進(jìn)階合并,參考我之前寫(xiě)的《Linux通用塊層介紹(part2: request層)》中對(duì)進(jìn)階合并的描述,如果bio產(chǎn)生了后向合并,則調(diào)用attempt_back_merge試圖進(jìn)行后向進(jìn)階合并,如果bio產(chǎn)生了前向合并,則調(diào)用attempt_front_merge企圖進(jìn)行前向進(jìn)階合并。deadline的進(jìn)階合并接口為deadline_merged_requests, 被合并的request會(huì)從調(diào)度隊(duì)列中刪除。通過(guò)下面的圖示來(lái)展示后向進(jìn)階合并過(guò)程,前向進(jìn)階合并同理。

3.如果產(chǎn)生了進(jìn)階合并,則被合并的request可以釋放了,參考上圖,可調(diào)用blk_put_request進(jìn)行回收。如果只產(chǎn)生了bio合并,合并后的request的長(zhǎng)度和扇區(qū)地址都會(huì)發(fā)生變化,需要調(diào)用elv_merged_request->elevator_merged_fn來(lái)更新合并后的請(qǐng)求在調(diào)度隊(duì)列的位置。deadline對(duì)應(yīng)的接口為deadline_merged_request,其相應(yīng)的操作為將合并的request先從調(diào)度隊(duì)列移出再重新插進(jìn)去。

“bio合并到elevator”的合并形式只會(huì)發(fā)生在進(jìn)程間,即只有一個(gè)進(jìn)程在IO的時(shí)候不會(huì)產(chǎn)生這種合并形式,原因在于進(jìn)程在向調(diào)度隊(duì)列派發(fā)IO請(qǐng)求或者試圖與將bio與調(diào)度隊(duì)列中的請(qǐng)求合并的時(shí)候是持有設(shè)備的隊(duì)列鎖得,其他進(jìn)程是不能往調(diào)度隊(duì)列派發(fā)請(qǐng)求,這也是通用塊層單隊(duì)列通道窄需要發(fā)展多隊(duì)列的主要原因之一,只有進(jìn)程在將調(diào)度隊(duì)列中的request逐個(gè)派發(fā)給驅(qū)動(dòng)層的時(shí)候才會(huì)將設(shè)備隊(duì)列鎖重新打開(kāi),即只有當(dāng)一個(gè)進(jìn)程在將調(diào)度隊(duì)列中request派發(fā)給驅(qū)動(dòng)的時(shí)候其他進(jìn)程才有機(jī)會(huì)將bio合并到還未派發(fā)完的request中。所以想通過(guò)簡(jiǎn)單的IO測(cè)試程序來(lái)捕捉這種形式的合并比較困難,這對(duì)兩個(gè)IO進(jìn)程的IO產(chǎn)生時(shí)序有非常高的要求,故不演示。有興趣的可以參考上面的github倉(cāng)庫(kù),里面有patch對(duì)內(nèi)核特定的請(qǐng)求派發(fā)位置加上延時(shí)來(lái)改變IO請(qǐng)求本來(lái)的時(shí)序,從而讓測(cè)試程序人為的達(dá)到這種碰撞效果。

request在泄流的時(shí)候合并到elevator

通用塊層的泄流接口為:blk_flush_plug_list(), 該接口主要的處理邏輯如下圖所示

其中請(qǐng)求合并發(fā)生的點(diǎn)在__elv_add_request()。blk_flush_plug_list會(huì)遍歷蓄流鏈表中的每個(gè)request,然后將每個(gè)request通過(guò) _elv_add_request接口添加到調(diào)度隊(duì)列中,添加的過(guò)程中會(huì)嘗試與調(diào)度隊(duì)列中已有的request進(jìn)行合并。

__elv_add_request:

case ELEVATOR_INSERT_SORT_MERGE:

/*

* If we succeed in merging this request with one in the

* queue already, we are done - rq has now been freed,

* so no need to do anything further.

*/

if (elv_attempt_insert_merge(q, rq))

break;

/* fall through */

case ELEVATOR_INSERT_SORT:

BUG_ON(blk_rq_is_passthrough(rq));

rq->rq_flags |= RQF_SORTED;

q->nr_sorted++;

if (rq_mergeable(rq)) {

elv_rqhash_add(q, rq);

if (!q->last_merge)

q->last_merge = rq;

}

q->elevator->type->ops.sq.elevator_add_req_fn(q, rq);

break;

泄流時(shí)走的是ELEVATOR_INSERT_SORT_MERGE分支,正如注釋所說(shuō)的先讓蓄流的request調(diào)用elv_attempt_insert_merge嘗試與調(diào)度隊(duì)列中的request合并,如果不能合并則落入到ELEVATOR_INSERT_SORT分支,該分支直接調(diào)用電梯調(diào)度器注冊(cè)的elevator_add_req_fn接口將新來(lái)的request插入到調(diào)度隊(duì)列合適的位置。其中elv_rqhash_add是將新加入到調(diào)度隊(duì)列的request做hash索引,這樣做的好處是加快從調(diào)度隊(duì)列尋找可合并的request的索引速度。在泄流的時(shí)候調(diào)度隊(duì)列中既有其他進(jìn)程產(chǎn)生的request,也有當(dāng)前進(jìn)程從蓄流鏈表中派發(fā)的request(blk_flush_plug_list是先將所有request派發(fā)到調(diào)度隊(duì)列再一次性queue_unplugged,而不是派發(fā)一個(gè)request就queue_unplugged)。所以“request在泄流的時(shí)候合并到elevator”既是進(jìn)程內(nèi)的,也可以是進(jìn)程間的。elv_attempt_insert_merge的實(shí)現(xiàn)只做request間的后向合并,即只會(huì)將一個(gè)request合并到調(diào)度隊(duì)列中的request的尾部。這對(duì)于單進(jìn)程IO而言足夠了,因?yàn)閎lk_flush_plug_list在泄流的時(shí)候已經(jīng)將蓄流鏈表中的request進(jìn)行了list_sort(按扇區(qū)排序)。筆者曾經(jīng)提交過(guò)促進(jìn)進(jìn)程間request的前向合并的patch(見(jiàn)github),但沒(méi)被接收,maintainer–Jens的解析是這種IO場(chǎng)景很難發(fā)生,如果產(chǎn)生這種IO場(chǎng)景基本是應(yīng)用程序設(shè)計(jì)不合理。通過(guò)增加時(shí)間和空間來(lái)優(yōu)化一個(gè)并不常見(jiàn)的場(chǎng)景并不可取。最后通過(guò)一個(gè)例子來(lái)驗(yàn)證進(jìn)程內(nèi)“request在泄流的時(shí)候合并到elevator”,進(jìn)程間的合并同樣對(duì)請(qǐng)求派發(fā)時(shí)序有很強(qiáng)的要求,在此不演示,github中有相應(yīng)的測(cè)試patch和測(cè)試方法。iotc使用下面的方式派發(fā)三個(gè)寫(xiě)io:

# ./iotc-d/dev/sdb-i

-i指定IO方式為INTERLEAVE_IO(交替),表示按扇區(qū)交替的方式發(fā)起三個(gè)寫(xiě)請(qǐng)求: bio0(16 + 8),bio1(8 + 8), bio2(0 + 8)。blktrace的觀察結(jié)果為:

blktrace -d /dev/sdb -o - | blkparse -i -

上面的輸出可以簡(jiǎn)單解析為:

bio0(16 + 8)先到達(dá)plug list,bio1(0+8)到達(dá)時(shí)發(fā)現(xiàn)不能與plug list中的request合并,于是申請(qǐng)一個(gè)request添加到plug list。bio2(8+8)到達(dá)時(shí)首先與bio1進(jìn)行后向合并。之后進(jìn)程觸發(fā)泄流,泄流接口函數(shù)會(huì)將plug list中的request排序,因此request(0+16)先派發(fā)到調(diào)度隊(duì)列,此時(shí)調(diào)度隊(duì)列為空不能進(jìn)行合并。然后派發(fā)request(16+8),派發(fā)時(shí)調(diào)用elv_attempt_insert_merge接口嘗試與調(diào)度隊(duì)列中的其他request進(jìn)行合并,發(fā)現(xiàn)可以與request(0+16)進(jìn)行后向合并,于是兩個(gè)request合并成一個(gè),最后向設(shè)備驅(qū)動(dòng)派發(fā)的只有一個(gè)request(0+24)。整個(gè)過(guò)程可以用下面的圖來(lái)展示:

小結(jié)

通過(guò)cache 、plug和elevator自上而下的三層狙擊,應(yīng)用程序產(chǎn)生的IO能最大限度的進(jìn)行合并,從而提升IO帶寬,降低IO延遲,延長(zhǎng)設(shè)備壽命。page cache打頭陣,既做數(shù)據(jù)緩存又做IO合并,主要是針對(duì)小塊IO進(jìn)行合并,因?yàn)槭褂脙?nèi)存頁(yè)做緩存,所以合并后的最大IO單元為頁(yè)大小,當(dāng)然對(duì)于大塊IO,page cache也會(huì)將它拆分成以頁(yè)為單位下發(fā),這不影響最終的效果,因?yàn)楹竺孢€有plug 和 elevator補(bǔ)刀。plug list竭盡全力合并進(jìn)程內(nèi)產(chǎn)生的IO, 從設(shè)備的角度而言進(jìn)程內(nèi)產(chǎn)生的IO相關(guān)性更強(qiáng),合并的可能性更大,plug list設(shè)計(jì)位于elevator queue之上而且又是每個(gè)進(jìn)程私有的,因此plug list既有利于IO合并,又減輕了elevator queue的負(fù)擔(dān)。elevator queue更多的是承擔(dān)進(jìn)程間IO的合并,用來(lái)彌補(bǔ)plug list對(duì)進(jìn)程間合并的不足,如果是帶緩存的IO,這種IO合并基本上不會(huì)出現(xiàn)。從實(shí)際應(yīng)用角度出發(fā),IO合并更多的是發(fā)生在page cache和plug list中。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • IO
    IO
    +關(guān)注

    關(guān)注

    0

    文章

    475

    瀏覽量

    39668
  • Cache
    +關(guān)注

    關(guān)注

    0

    文章

    129

    瀏覽量

    28558

原文標(biāo)題:劉正元: Linux 通用塊層之IO合并

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    for always可以block中合成的嗎?

    (genvar)塊”和一個(gè)“always @ block”,我兩者中都有“for loops”。我的問(wèn)題是這些“for loops”可以合成FPGA實(shí)現(xiàn)嗎?我知道Genvar塊是硬件,沒(méi)關(guān)系。我關(guān)注
    發(fā)表于 10-30 11:11

    Vivado 2015.2塊設(shè)計(jì)上打開(kāi)子層次結(jié)構(gòu)彈出一個(gè)新的Block Design窗口

    假設(shè)我Vivado 2015.2的Block Design中有三層設(shè)計(jì)。此塊設(shè)計(jì)看起來(lái)像Hierarchy_0(Hierarchy_1(Hierarchy_2))。當(dāng)我雙擊
    發(fā)表于 12-25 10:58

    請(qǐng)問(wèn)AD09如何為top layer 和bottom layer單獨(dú)設(shè)置keep out layer的大小?

    AD09如何為top layer 和bottom layer 單獨(dú)設(shè)置keep out layer的大小
    發(fā)表于 05-27 01:05

    layer3編碼源碼

    layer3編碼源碼
    發(fā)表于 04-08 03:20 ?32次下載

    wcdma-physical layer

    wcdma-physical layer:
    發(fā)表于 06-04 17:27 ?22次下載
    wcdma-physical <b class='flag-5'>layer</b>

    CAN Physical Layer for Industr

    CAN Physical Layer for Industrial Application:Two-Wire Differential Transmission1. ScopeThe scope
    發(fā)表于 09-21 15:47 ?12次下載

    A CAN Physical Layer Discussio

    A CAN Physical Layer Discussion:Many network protocols are described using the sevenlayer Open
    發(fā)表于 10-01 16:59 ?10次下載

    什么是Transport Layer Security

    什么是Transport Layer Security   術(shù)語(yǔ)名稱:Transport Layer Security 術(shù)語(yǔ)解釋:傳輸層安全,確保無(wú)線局域網(wǎng)和因特網(wǎng)上通
    發(fā)表于 02-24 09:59 ?924次閱讀

    MAX14820 IO-Link設(shè)備收發(fā)器

    specifiedIO-Link data rates are supported. In IO-Link applications,the transceiver acts as the physical layer interfaceto a microcontro
    發(fā)表于 03-19 17:38 ?38次下載
    MAX14820 <b class='flag-5'>IO</b>-Link設(shè)備收發(fā)器

    http請(qǐng)求 get post

    Http請(qǐng)求類 packagewzh.Http; importjava.io.BufferedReader; importjava.io
    發(fā)表于 09-27 10:36 ?18次下載

    PCIe的Spec中明確規(guī)定只有Root有權(quán)限發(fā)起配置請(qǐng)求

    處理器一般不能夠直接發(fā)起配置讀寫(xiě)請(qǐng)求,因?yàn)槠渲荒墚a(chǎn)生Memory Request和IO Request。這就意味著Root必須要將處理器的相關(guān)請(qǐng)求轉(zhuǎn)換為配置讀寫(xiě)請(qǐng)求。針對(duì)傳統(tǒng)的PCI設(shè)
    的頭像 發(fā)表于 05-04 09:12 ?7089次閱讀
    PCIe的Spec中明確規(guī)定只有Root有權(quán)限發(fā)起配置<b class='flag-5'>請(qǐng)求</b>

    layer是什么?解析ad9中的plane與layer

    layer是什么?PCB設(shè)計(jì)中layer是什么意思?PCB設(shè)計(jì)中多層板的層設(shè)置當(dāng)初困擾了很久,就是沒(méi)搞懂plane和layer的區(qū)別。 Multi-
    的頭像 發(fā)表于 10-16 11:37 ?8603次閱讀

    ACIS內(nèi)核和parasolid內(nèi)核的來(lái)龍去脈與比較

    ACIS內(nèi)核和parasolid內(nèi)核的來(lái)龍去脈與比較(深圳市普德新星電源技術(shù)有限公司)-ACIS內(nèi)核和parasolid內(nèi)核的來(lái)龍去脈與比較 ? ? ? ? ? ? ??
    發(fā)表于 08-31 16:52 ?11次下載
    ACIS內(nèi)核和parasolid內(nèi)核的<b class='flag-5'>來(lái)龍去脈</b>與比較

    查看linux系統(tǒng)磁盤io情況的辦法是什么

    談到 Linux 磁盤 I/O 的工作原理,我們了解到 Linux 存儲(chǔ)系統(tǒng) I/O 棧由文件系統(tǒng)層(file system layer)、通用塊層( general block layer)和設(shè)備層(device
    發(fā)表于 08-01 10:14 ?2472次閱讀

    什么是io多路復(fù)用?IO多路復(fù)用的優(yōu)缺點(diǎn)

    IO多路復(fù)用是一種同步IO模型,它允許單個(gè)進(jìn)程/線程同時(shí)處理多個(gè)IO請(qǐng)求。具體來(lái)說(shuō),一個(gè)進(jìn)程/線程可以監(jiān)視多個(gè)文件句柄,一旦某個(gè)文件句柄就緒,就能夠通知應(yīng)用程序進(jìn)行相應(yīng)的讀寫(xiě)操作。
    的頭像 發(fā)表于 01-18 15:48 ?1831次閱讀
    主站蜘蛛池模板: 免费中文字幕视频 | 久久久精品久久久久三级 | 色视频色露露永久免费观看 | 99久久人妻无码精品系列性欧美 | 男人和女人一起愁愁愁很痛 | 精品高潮呻吟99AV无码视频 | 蜜桃狠狠色伊人亚洲综合网站 | 2021国产精品久久久久精品免费网 | 国产精品一区二区资源 | 69国产精品成人无码视频 | 欧美激情精品久久久久久不卡 | 欧美午夜理伦三级在线观看 | 麻豆沈芯语| 一本道无码字幕在线看 | 野花韩国高清完整版在线 | 快播电影网址 | 51久久夜色精品国产 | 成人无码在线超碰视频 | 久久亚洲人成国产精品 | 久久不射网 | GOGOGO高清在线播放免费 | 日韩亚洲欧洲在线rrrr片 | 亚洲宅男天堂a在线 | 久久精品视在线观看85 | 欧美激情一区二区三区视频 | 我不卡影院手机在线观看 | 暖暖日本在线手机免费完整版 | 色男人综合| 99日精品欧美国产 | japanese幼儿videos| 欧美黑大炮18p | nu77亚洲综合日韩精品 | 亚洲一区二区三区乱码在线欧洲 | 亚洲伊人久久网 | 在线看片福利无码网址 | 果冻传媒视频在线播放 免费观看 | 少妇精品久久久一区二区三区 | 国产精品美女久久久网站动漫 | 国产精品日本欧美一区二区 | 一级毛片皇帝 宫女 | 亚洲中文字幕国产综合 |