Linux下的塊IO層

如果硬件設(shè)備是以數(shù)據(jù)流的方式訪問的囚企,它就是字符設(shè)備(character device)铝侵,但如果是隨機訪問的就是塊設(shè)備(block device)予跌,典型的如硬盤

sector(扇區(qū))

The smallest addressable unit on a block device is a sector. Sectors come in various powers of two, but 512 bytes is the most common size
塊設(shè)備上最小訪問單元是扇區(qū)(sector)榆纽,一般512字節(jié)大小

雖然最小物理訪問單元是扇區(qū)动遭,但是軟件的最小邏輯訪問單元是塊(block)芬探,它是扇區(qū)大小的2的冪次方倍,一般為512 bytes, 1 kilobyte, and 4 kilobytes厘惦,但不會超過頁大型捣隆(人為限制),那是管理內(nèi)存的最小單位宵蕉。

block(塊)

文件系統(tǒng)都是以塊為單位來操作磁盤的酝静,CPU是不能直接操作磁盤上的數(shù)據(jù),必須先把它們讀到內(nèi)存中羡玛,那么內(nèi)存中存儲一塊大小的數(shù)據(jù)叫做buffer别智,buffer就是磁盤上一塊大小的數(shù)據(jù)在內(nèi)存中表示,那這個buffer對應(yīng)哪個塊設(shè)備上的哪塊就需要內(nèi)核來管理稼稿,內(nèi)核使用buffer_head這個數(shù)據(jù)結(jié)構(gòu)來描述block和buffer的關(guān)系

struct buffer_head {
unsigned long b_state; /* buffer state flags */
struct buffer_head *b_this_page; /* list of page’s buffers */
struct page *b_page; /* associated page 塊對應(yīng)的物理頁 */
sector_t b_blocknr;/* starting block number */
size_t b_size;/* size of mapping 塊的大小*/
char *b_data;/* pointer to data within the page 塊的地址*/ 
struct block_device *b_bdev;/* associated block device */
bh_end_io_t *b_end_io;/* I/O completion */
void *b_private;/* reserved for b_end_io */
struct list_head b_assoc_buffers; /* associated mappings */ 
struct address_space *b_assoc_map; /* associated address space */ 
atomic_t b_count; /* use count */
};

在2.6之前薄榛,buffer_header在內(nèi)核中得以重用讳窟,它是塊設(shè)備IO的單位和容器,但這個結(jié)構(gòu)有點大,用它來操作數(shù)據(jù)既不簡潔也不簡單敞恋,內(nèi)核更擅長用頁來處理丽啡,這樣既簡單又高效;另一方面buffer_header只能描述單個buffer硬猫,如果要操作大塊數(shù)據(jù)勢必要分成多個buffer_header补箍,導(dǎo)致無謂的開銷。于是在2.5版本就重點開發(fā)了bio結(jié)構(gòu)作為block I/O操作的容器

bio結(jié)構(gòu)

bio數(shù)據(jù)結(jié)構(gòu)以段的形式將buffers組織在一起啸蜜,這些buffers在內(nèi)存中不必是連續(xù)的

struct bio {
...
unsigned short bi_vcnt;/* number of bio_vec */
struct bio_vec *bi_io_vec; /* bio_vec list */
...
}

bio結(jié)構(gòu)體含有bio_vec數(shù)組坑雅,圖上它們指向某個page,但并不表示指向整個page盔性,而是其中一段霞丧,每個 bio_vec 就是這樣一個三元組(page, offset, len), 哪塊物理頁呢岗,偏移多少字節(jié)冕香、長度多少就定位了一塊buffer

bio
struct bio_vec {
    struct page *bv_page;/* pointer to the physical page on which this buffer resides */
    unsigned int bv_len;/* the length in bytes of this buffer */ 
    unsigned int bv_offset;/* the byte offset within the page where the buffer resides */ 
};

bio結(jié)構(gòu)體的優(yōu)點

  • 可以輕松表示高內(nèi)存,因為它只處理物理頁而不是指針
  • 既能表示普通page I/O也能表示direct I/O(不需要頁緩存)
  • 很容易實現(xiàn)scatter-gatter I/O 塊操作
  • 相比buffer_header,僅包含I/O 塊操作的最小信息后豫,不會包含與buffer無關(guān)的信息

但bio并不能替代buffer_header,它不能描述buffer的狀態(tài)悉尾,所以需要兩者共存互補。

IO調(diào)度器

一個運行的系統(tǒng)挫酿,每時每刻都會有不同的進程發(fā)起IO請求构眯,這些請求都會放在一個請求隊列中,用 <linux/blkdev.h>中的request_queue結(jié)構(gòu)體表示早龟,隊列中的每個請求用request結(jié)構(gòu)體表示惫霸,它是由多個bio結(jié)構(gòu)體組成。
計算機中的硬盤是為數(shù)不多的機械裝置葱弟,它讀寫數(shù)據(jù)的速度與內(nèi)存相比都是上萬倍的差距壹店,由于是機械裝置,磁頭對扇區(qū)的尋址是計算機中最慢的操作之一芝加,如果對IO請求逐個處理不做全局優(yōu)化硅卢,計算機的性能將顯著降低,比如下面一個請求隊列

  • 騎自行車到北京買包中南海
  • 騎自行車到上海買包華子
  • 騎自行車到北京買一條中南海
  • 騎自行車到上海買一條華子

所以IO調(diào)度器的一個重點就是全局考慮隊列的請求藏杖,優(yōu)化尋址時間将塑。

The Linus Elevator

Linux中的最簡單的調(diào)度算法就是電梯調(diào)度算法,主要有四點

  1. If a request to an adjacent on-disk sector is in the queue, the existing request and the new request merge into a single request.如果IO請求的扇區(qū)和能在隊列中找到毗鄰的蝌麸,合并成一個
  2. If a request in the queue is sufficiently old, the new request is inserted at the tail of the queue to prevent starvation of the other, older, requests.如果第一步找到的請求存在的時間有點久遠會放棄合并插到隊尾点寥,否則對同一位置的頻繁請求會導(dǎo)致后面的請求無法響應(yīng)
  3. If a suitable location sector-wise is in the queue, the new request is inserted there. This keeps the queue sorted by physical location on disk.調(diào)度算法會按照請求的扇區(qū)對請求排序,根據(jù)新請求扇區(qū)的位置將其插入到隊列中合適的位置来吩,這樣隊列中請求的扇區(qū)物理位置是有序的敢辩,磁頭像電梯一樣一次只朝一個方向移動處理請求汉柒,避免來回?zé)o序的尋址。
  4. Finally, if no such suitable insertion point exists, the request is inserted at the tail of the queue.最后如果找不到合適位置就會插在隊尾

The Deadline(截止日期) I/O Scheduler

電梯調(diào)度算法雖然顧全大局责鳍,能極有效的處理磁頭移動碾褂,但仍然沒解決請求餓死的問題,首先說說數(shù)據(jù)的讀或?qū)懙奶卣骼穑瑢嶋H上它們截然不同

  1. 進程是不可以直接將數(shù)據(jù)寫到磁盤的正塌,進程只是把寫請求和相關(guān)數(shù)據(jù)等信息提交到內(nèi)核,然后就返回了恤溶,是異步的乓诽,內(nèi)核為了性能也不會立馬將數(shù)據(jù)寫回磁盤,它有一個專門的進程周期性的將緩存的數(shù)據(jù)一起寫回磁盤咒程,所以通常是較大的數(shù)據(jù)流鸠天,這些寫入請求傾向于寫到磁盤相近的地方,很符合電梯調(diào)度算法的胃口帐姻,導(dǎo)致這個算法傾向于滿頭苦寫無法自拔

  2. 進程也是不能直接讀取磁盤上的數(shù)據(jù)稠集,它要發(fā)起讀請求,讓內(nèi)核去撈數(shù)據(jù)饥瓷,但它可不會像寫那樣就返回了剥纷,它要等內(nèi)核給它數(shù)據(jù)才會返回,所以讀操作是同步的呢铆,對等待時長很敏感晦鞋。與寫相比,讀的數(shù)據(jù)通常較小而且不連續(xù)棺克,比如目錄下的文件悠垛,有碎片化的特征,更要命的是還不能同時進行娜谊,這個沒返回是不能讀下一個的确买,有依賴關(guān)系。

由于讀寫的這種矛盾因俐,流式寫請求容易讓后面的讀請求在合理的時間內(nèi)得不到處理拇惋,而產(chǎn)生寫?zhàn)I死讀的問題,比如下面一個請求隊列

  • 騎自行車到北京大興送貨
  • 騎自行車到北京豐臺送貨
  • 騎自行車到北京石景山送貨
  • 騎自行車到北京海淀買送貨
  • 騎自行車到北京朝陽送貨
  • 騎自行車到北京東城送貨
  • 騎自行車到上海買包華子

Deadline調(diào)度器在電梯調(diào)度器的Sorted queue基礎(chǔ)上添加了兩個先入先出隊列抹剩,并且每個新請求會設(shè)置一個有效期(expiration time), 讀請求是500ms撑帖,寫請求是5s,新請求不僅會正常插入到Sorted queue澳眷,還會添加到另一個隊列的尾部(如:讀請求到Read FIFO queue)胡嘿,調(diào)度器處理請求和以前一樣,從Sorted queue獲取請求钳踊,但如果Read隊列或Write隊列的隊頭請求要到期了就需要優(yōu)先處理衷敌,也就是讀隊列的請求最久500ms左右能被處理勿侯,而寫隊列是5s,本質(zhì)上就是讓讀請求的優(yōu)先級高于寫請求缴罗,從而避免寫?zhàn)I死讀的情況發(fā)生助琐。

Deadline Scheduler

Anticipatory(預(yù)料的) I/O Scheduler

Deadline調(diào)度器在性能和延時兩端做了相當好的平衡,但讀請求碎片化面氓、獨立性兵钮、需同步的特性使得磁頭難免不斷的來回移動,付出昂貴的代價舌界。比如正在處理一堆寫請求流掘譬,間歇的來了一波讀請求(進程在一個讀請求結(jié)束后才會發(fā)起下一個讀請求),如果兩種請求涉及的扇區(qū)較遠呻拌,那么就有大量時間花費在來回移動磁頭上面葱轩。
Anticipatory就是為了解決這個問題,它在Deadline調(diào)度器的基礎(chǔ)上添加了預(yù)判機制藐握,在處理完讀請求后并不著急離開靴拱,而是賭一把等上6ms,看這個時間內(nèi)能不能等到下個讀請求趾娃,如果等到了就立馬處理然后繼續(xù)等待缭嫡,否則承認猜錯了移動磁頭返回原處缔御。為了提高命中率而不是瞎猜抬闷,Anticipatory調(diào)度器會統(tǒng)計每個進程的IO信息來跟蹤它們的行為,從而做出智能預(yù)判耕突。

以上整理自:
Linux kernel Development - Robert Love
I/O Schedulers

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笤成,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子眷茁,更是在濱河造成了極大的恐慌炕泳,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件上祈,死亡現(xiàn)場離奇詭異培遵,居然都是意外死亡,警方通過查閱死者的電腦和手機登刺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門籽腕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纸俭,你說我怎么就攤上這事皇耗。” “怎么了揍很?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵郎楼,是天一觀的道長万伤。 經(jīng)常有香客問我,道長呜袁,這世上最難降的妖魔是什么敌买? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮阶界,結(jié)果婚禮上放妈,老公的妹妹穿的比我還像新娘。我一直安慰自己荐操,他們只是感情好芜抒,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著托启,像睡著了一般宅倒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屯耸,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天拐迁,我揣著相機與錄音,去河邊找鬼疗绣。 笑死线召,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的多矮。 我是一名探鬼主播缓淹,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼塔逃!你這毒婦竟也來了讯壶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤湾盗,失蹤者是張志新(化名)和其女友劉穎伏蚊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體格粪,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡躏吊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帐萎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片比伏。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吓肋,靈堂內(nèi)的尸體忽然破棺而出凳怨,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布肤舞,位于F島的核電站紫新,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏李剖。R本人自食惡果不足惜芒率,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篙顺。 院中可真熱鬧偶芍,春花似錦、人聲如沸德玫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宰僧。三九已至材彪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琴儿,已是汗流浹背段化。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留造成,地道東北人显熏。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像晒屎,于是被迫代替她去往敵國和親喘蟆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Linux下夷磕,I/O處理的層次可分為4層: 系統(tǒng)調(diào)用層履肃,應(yīng)用程序使用系統(tǒng)調(diào)用指定讀寫哪個文件,文件偏移是多少 文件...
    tracy_668閱讀 1,529評論 0 1
  • 轉(zhuǎn)載自:http://blog.sina.com.cn/s/blog_416656f70102vwld.html本...
    SkTj閱讀 1,036評論 0 0
  • 在進行Linux內(nèi)核配置時有下述工具可以使用:make config:該工具會逐一便利所有配置項坐桩,要求用戶進行選擇...
    某WAP閱讀 269評論 0 0
  • Linux 本地文件存儲原理 在LINUX系統(tǒng)中有一個重要的概念:一切都是文件。UNIX系統(tǒng)把每個硬件都看成是一個...
    睡不醒的大橘閱讀 896評論 0 0
  • 進程 創(chuàng)建 創(chuàng)建進程用fork()函數(shù)封锉。fork()為子進程創(chuàng)建新的地址空間并且拷貝頁表绵跷。子進程的虛擬地址空間...
    梅花怒閱讀 1,914評論 0 7