Linux IO Scheduler(Linux IO 調度器)


     每個塊設備或者塊設備的分區,都對應有自身的請求隊列(request_queue),而每個請求隊列都可以選擇一個I/O調度器來協調所遞交的requestI/O調度器的基本目的是將請求按照它們對應在塊設備上的扇區號進行排列,以減少磁頭的移動,提高效率。每個設備的請求隊列里的請求將按順序被響應。實際上,除了這個隊列,每個調度器自身都維護有不同數量的隊列,用來對遞交上來的request進行處理,而排在隊列最前面的request將適時被移動到請求隊列中等待響應。

     IO調度器在內核棧中所處位置如下:

      

      內核中實現的IO調度器主要有四種--Noop,Deadline,CFG, Anticipatory。

1,Noop算法

     Noop調度算法是內核中最簡單的IO調度算法。Noop調度算法也叫作電梯調度算法,它將IO請求放入到一個FIFO隊列中,然后逐個執行這些IO請求,當然對於一些在磁盤上連續的IO請求,Noop算法會適當做一些合並。這個調度算法特別適合那些不希望調度器重新組織IO請求順序的應用。

     這種調度算法在以下場景中優勢比較明顯:

     1)在IO調度器下方有更加智能的IO調度設備。如果您的Block Device Drivers是Raid,或者SAN,NAS等存儲設備,這些設備會更好地組織IO請求,不用IO調度器去做額外的調度工作;

     2)上層的應用程序比IO調度器更懂底層設備。或者說上層應用程序到達IO調度器的IO請求已經是它經過精心優化的,那么IO調度器就不需要畫蛇添足,只需要按序執行上層傳達下來的IO請求即可。

     3)對於一些非旋轉磁頭氏的存儲設備,使用Noop的效果更好。因為對於旋轉磁頭式的磁盤來說,IO調度器的請求重組要花費一定的CPU時間,但是對於SSD磁盤來說,這些重組IO請求的CPU時間可以節省下來,因為SSD提供了更智能的請求調度算法,不需要內核去畫蛇添足。這篇文章提及了SSD中使用Noop效果會更好。

2,Deadline算法

     Deadline算法的核心在於保證每個IO請求在一定的時間內一定要被服務到,以此來避免某個請求飢餓。

     Deadline算法中引入了四個隊列,這四個隊列可以分為兩類,每一類都由讀和寫兩類隊列組成,一類隊列用來對請求按起始扇區序號進行排序,通過紅黑樹來組織,稱為sort_list;另一類對請求按它們的生成時間進行排序,由鏈表來組織,稱為fifo_list。每當確定了一個傳輸方向(讀或寫),那么將會從相應的sort_list中將一批連續請求dispatch到requst_queue的請求隊列里,具體的數目由fifo_batch來確定。只有下面三種情況才會導致一次批量傳輸的結束:

    1)對應的sort_list中已經沒有請求了

    2)下一個請求的扇區不滿足遞增的要求

    3)上一個請求已經是批量傳輸的最后一個請求了。

     所有的請求在生成時都會被賦上一個期限值(根據jiffies),並按期限值排序在fifo_list中,讀請求的期限時長默認為為500ms,寫請求的期限時長默認為5s,可以看出內核對讀請求是十分偏心的,其實不僅如此,在deadline調度器中,還定義了一個starved和writes_starved,writes_starved默認為2,可以理解為寫請求的飢餓線,內核總是優先處理讀請求,starved表明當前處理的讀請求批數,只有starved超過了writes_starved后,才會去考慮寫請求。因此,假如一個寫請求的期限已經超過,該請求也不一定會被立刻響應,因為讀請求的batch還沒處理完,即使處理完,也必須等到starved超過writes_starved才有機會被響應。為什么內核會偏袒讀請求?這是從整體性能上進行考慮的。讀請求和應用程序的關系是同步的,因為應用程序要等待讀取的內容完畢,才能進行下一步工作,因此讀請求會阻塞進程,而寫請求則不一樣,應用程序發出寫請求后,內存的內容何時寫入塊設備對程序的影響並不大,所以調度器會優先處理讀請求。

     默認情況下,讀請求的超時時間是500ms,寫請求的超時時間是5s。

     這篇文章說在一些多線程應用下,Deadline算法比CFQ算法好。這篇文章說在一些數據庫應用下,Deadline算法比CFQ算法好。

3,Anticipatory算法

    Anticipatory算法的核心是局部性原理,它期望一個進程昨晚一次IO請求后還會繼續在此處做IO請求。在IO操作中,有一種現象叫“假空閑”(Deceptive idleness),它的意思是一個進程在剛剛做完一波讀操作后,看似是空閑了,不讀了,但是實際上它是在處理這些數據,處理完這些數據之后,它還會接着讀,這個時候如果IO調度器去處理另外一個進程的請求,那么當原來的假空閑進程的下一個請求來的時候,磁頭又得seek到剛才的位置,這樣大大增加了尋道時間和磁頭旋轉時間。所以,Anticipatory算法會在一個讀請求做完后,再等待一定時間t(通常是6ms),如果6ms內,這個進程上還有讀請求過來,那么我繼續服務,否則,處理下一個進程的讀寫請求。

     在一些場景下,Antocipatory算法會有非常有效的性能提升。這篇文章有說,這篇文章也有一份評測。

      值得一提的是,Anticipatory算法從Linux 2.6.33版本后,就被移除了,因為CFQ通過配置也能達到Anticipatory算法的效果。

4,CFQ算法

    CFQ(Completely Fair Queuing)算法,顧名思義,絕對公平算法。它試圖為競爭塊設備使用權的所有進程分配一個請求隊列和一個時間片,在調度器分配給進程的時間片內,進程可以將其讀寫請求發送給底層塊設備,當進程的時間片消耗完,進程的請求隊列將被掛起,等待調度。 每個進程的時間片和每個進程的隊列長度取決於進程的IO優先級,每個進程都會有一個IO優先級,CFQ調度器將會將其作為考慮的因素之一,來確定該進程的請求隊列何時可以獲取塊設備的使用權。IO優先級從高到低可以分為三大類:RT(real time),BE(best try),IDLE(idle),其中RTBE又可以再划分為8個子優先級。實際上,我們已經知道CFQ調度器的公平是針對於進程而言的,而只有同步請求(readsyn write)才是針對進程而存在的,他們會放入進程自身的請求隊列,而所有同優先級的異步請求,無論來自於哪個進程,都會被放入公共的隊列,異步請求的隊列總共有8(RT)+8(BE)+1(IDLE)=17個。

    從Linux 2.6.18起,CFQ作為默認的IO調度算法。

    對於通用的服務器來說,CFQ是較好的選擇。

 

對於使用哪種調度算法來說,還是要根據具體的業務場景去做足benchmark來選擇,不能僅靠別人的文字來決定。

5,更改IO調度算法

RHEL5/OEL5以及之后的版本中(比如RHEL6和RHEL7),可以針對每塊磁盤制定I/O Scheduler,修改完畢立刻生效,比如:

$ cat /sys/block/sda1/queue/scheduler
[noop] anticipatory deadline cfq

#修改為cfq
$ echo 'cfq'>/sys/block/sda1/queue/scheduler

#立刻生效
$ cat /sys/block/sda1/queue/scheduler
noop anticipatory deadline [cfq]

 

6,一些磁盤相關的內核參數

/sys/block/sda/queue/nr_requests 磁盤隊列長度。默認只有 128 個隊列,可以提高到 512 個.會更加占用內存,但能更加多的合並讀寫操作,速度變慢,但能讀寫更加多的量


/sys/block/sda/queue/iosched/antic_expire 等待時間 。讀取附近產生的新請時等待多長時間

 

/sys/block/sda/queue/read_ahead_kb
    這個參數對順序讀非常有用,意思是,一次提前讀多少內容,無論實際需要多少.默認一次讀 128kb 遠小於要讀的,設置大些對讀大文件非常有用,可以有效的減少讀 seek 的次數,這個參數可以使用 blockdev –setra 來設置,setra 設置的是多少個扇區,所以實際的字節是除以2,比如設置 512 ,實際是讀 256 個字節.
 
/proc/sys/vm/dirty_ratio

  這個參數控制文件系統的文件系統寫緩沖區的大小,單位是百分比,表示系統內存的百分比,表示當寫緩沖使用到系統內存多少的時候,開始向磁盤寫出數 據.增大之會使用更多系統內存用於磁盤寫緩沖,也可以極大提高系統的寫性能.但是,當你需要持續、恆定的寫入場合時,應該降低其數值,一般啟動上缺省是 10.下面是增大的方法: echo ’40’> 

 

/proc/sys/vm/dirty_background_ratio

  這個參數控制文件系統的pdflush進程,在何時刷新磁盤.單位是百分比,表示系統內存的百分比,意思是當寫緩沖使用到系統內存多少的時候, pdflush開始向磁盤寫出數據.增大之會使用更多系統內存用於磁盤寫緩沖,也可以極大提高系統的寫性能.但是,當你需要持續、恆定的寫入場合時,應該降低其數值,一般啟動上缺省是 5.下面是增大的方法: echo ’20’ >

 

 /proc/sys/vm/dirty_writeback_centisecs

    這個參數控制內核的臟數據刷新進程pdflush的運行間隔.單位是 1/100 秒.缺省數值是500,也就是 5 秒.如果你的系統是持續地寫入動作,那么實際上還是降低這個數值比較好,這樣可以把尖峰的寫操作削平成多次寫操作.設置方法如下: echo ‘200’ > /proc/sys/vm/dirty_writeback_centisecs 如果你的系統是短期地尖峰式的寫操作,並且寫入數據不大(幾十M/次)且內存有比較多富裕,那么應該增大此數值: echo ‘1000’ > /proc/sys/vm/dirty_writeback_centisecs

 

/proc/sys/vm/dirty_expire_centisecs

  這個參數聲明Linux內核寫緩沖區里面的數據多“舊”了之后,pdflush進程就開始考慮寫到磁盤中去.單位是 1/100秒.缺省是 30000,也就是 30 秒的數據就算舊了,將會刷新磁盤.對於特別重載的寫操作來說,這個值適當縮小也是好的,但也不能縮小太多,因為縮小太多也會導致IO提高太快.建議設置為 1500,也就是15秒算舊. echo ‘1500’ > /proc/sys/vm/dirty_expire_centisecs 當然,如果你的系統內存比較大,並且寫入模式是間歇式的,並且每次寫入的數據不大(比如幾十M),那么這個值還是大些的好.

 

參考文獻

1,https://en.wikipedia.org/wiki/Noop_scheduler

2,https://en.wikipedia.org/wiki/Deadline_scheduler

3,https://en.wikipedia.org/wiki/Anticipatory_scheduling

4,https://en.wikipedia.org/wiki/CFQ

5,http://www.redhat.com/magazine/008jun05/features/schedulers/ 這篇文章介紹了四種IO調度算法並且針對他們的應用場景做了一個評測

6,http://www.dbform.com/html/2011/1510.html

7,https://support.rackspace.com/how-to/configure-flash-drives-in-high-io-instances-as-data-drives/ 這篇文章介紹了一些SSD中IO scheduler的配置

8,https://www.percona.com/blog/2009/01/30/linux-schedulers-in-tpcc-like-benchmark/

9,http://www.ibm.com/support/knowledgecenter/api/content/linuxonibm/liaat/liaatbestpractices_pdf.pdf

10,http://dl.acm.org/citation.cfm?id=502046&dl=GUIDE&coll=GUIDE

11,http://www.nuodb.com/techblog/tuning-linux-io-scheduler-ssds

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM