每一個塊設備或者塊設備的分區,都對應有自身的請求隊列(request_queue),而每一個請求隊列均可以選擇一個I/O調度器來協調所遞交的request。I/O調度器的基本目的是將請求按照它們對應在塊設備上的扇區號進行排列,以減小磁頭的移動,提升效率。每一個設備的請求隊列裏的請求將按順序被響應。實際上,除了這個隊列,每一個調度器自身都維護有不一樣數量的隊列,用來對遞交上來的request進行處理,而排在隊列最前面的request將適時被移動到請求隊列中等待響應。html
IO調度器在內核棧中所處位置以下:node
內核中實現的IO調度器主要有四種--Noop,Deadline,CFG, Anticipatory。
linux
Noop調度算法是內核中最簡單的IO調度算法。Noop調度算法也叫做電梯調度算法,它將IO請求放入到一個FIFO隊列中,而後逐個執行這些IO請求,固然對於一些在磁盤上連續的IO請求,Noop算法會適當作一些合併。這個調度算法特別適合那些不但願調度器從新組織IO請求順序的應用。ios
這種調度算法在如下場景中優點比較明顯:算法
1)在IO調度器下方有更加智能的IO調度設備。若是您的Block Device Drivers是Raid,或者SAN,NAS等存儲設備,這些設備會更好地組織IO請求,不用IO調度器去作額外的調度工做;數據庫
2)上層的應用程序比IO調度器更懂底層設備。或者說上層應用程序到達IO調度器的IO請求已是它通過精心優化的,那麼IO調度器就不須要多此一舉,只須要按序執行上層傳達下來的IO請求便可。api
3)對於一些非旋轉磁頭氏的存儲設備,使用Noop的效果更好。由於對於旋轉磁頭式的磁盤來講,IO調度器的請求重組要花費必定的CPU時間,可是對於SSD磁盤來講,這些重組IO請求的CPU時間能夠節省下來,由於SSD提供了更智能的請求調度算法,不須要內核去多此一舉。這篇文章說起了SSD中使用Noop效果會更好。服務器
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算法好。
Anticipatory算法的核心是局部性原理,它指望一個進程昨晚一次IO請求後還會繼續在此處作IO請求。在IO操做中,有一種現象叫「假空閒」(Deceptive idleness),它的意思是一個進程在剛剛作完一波讀操做後,看似是空閒了,不讀了,可是實際上它是在處理這些數據,處理完這些數據以後,它還會接着讀,這個時候若是IO調度器去處理另一個進程的請求,那麼當原來的假空閒進程的下一個請求來的時候,磁頭又得seek到剛纔的位置,這樣大大增長了尋道時間和磁頭旋轉時間。因此,Anticipatory算法會在一個讀請求作完後,再等待必定時間t(一般是6ms),若是6ms內,這個進程上還有讀請求過來,那麼我繼續服務,不然,處理下一個進程的讀寫請求。
在一些場景下,Antocipatory算法會有很是有效的性能提高。這篇文章有說,這篇文章也有一份評測。
值得一提的是,Anticipatory算法從Linux 2.6.33版本後,就被移除了,由於CFQ經過配置也能達到Anticipatory算法的效果。
CFQ(Completely Fair Queuing)算法,顧名思義,絕對公平算法。它試圖爲競爭塊設備使用權的全部進程分配一個請求隊列和一個時間片,在調度器分配給進程的時間片內,進程能夠將其讀寫請求發送給底層塊設備,當進程的時間片消耗完,進程的請求隊列將被掛起,等待調度。 每一個進程的時間片和每一個進程的隊列長度取決於進程的IO優先級,每一個進程都會有一個IO優先級,CFQ調度器將會將其做爲考慮的因素之一,來肯定該進程的請求隊列什麼時候能夠獲取塊設備的使用權。IO優先級從高到低能夠分爲三大類:RT(real time),BE(best try),IDLE(idle),其中RT和BE又能夠再劃分爲8個子優先級。實際上,咱們已經知道CFQ調度器的公平是針對於進程而言的,而只有同步請求(read或syn write)纔是針對進程而存在的,他們會放入進程自身的請求隊列,而全部同優先級的異步請求,不管來自於哪一個進程,都會被放入公共的隊列,異步請求的隊列總共有8(RT)+8(BE)+1(IDLE)=17個。
從Linux 2.6.18起,CFQ做爲默認的IO調度算法。
對於通用的服務器來講,CFQ是較好的選擇。
對於使用哪一種調度算法來講,仍是要根據具體的業務場景去作足benchmark來選擇,不能僅靠別人的文字來決定。
在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 等待時間 。讀取附近產生的新請時等待多長時間
這個參數控制文件系統的文件系統寫緩衝區的大小,單位是百分比,表示系統內存的百分比,表示當寫緩衝使用到系統內存多少的時候,開始向磁盤寫出數 據.增大之會使用更多系統內存用於磁盤寫緩衝,也能夠極大提升系統的寫性能.可是,當你須要持續、恆定的寫入場合時,應該下降其數值,通常啓動上缺省是 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