linux的CFQ調度器解析(1)

來源:互聯網
上載者:User

CFQ調度器是四種IO Scheduler中最複雜的一個,redhat有個文檔可以做為入門的文檔先瞭解下 red-hat-enterprise-linux-5-io-tuning-guide.pdf

The cfq scheduler maintains a maximum of 64 internal request queues; each process running on the
system is assigned to any of these queues. Each time a process submits a synchronous I/
O request, it is moved to the assigned internal queue. Asynchronous requests from all processes are
batched together according to their process's I/O priority; for example, all asynchronous requests from
processes with a scheduling priority of "idle" (3) are put into one queue.

During each cycle, requests are moved from each non-empty internal request queue into one dispatch
queue. in a round-robin fashion. Once in the dispatch queue, requests are ordered to minimize disk
seeks and serviced accordingly.

To illustrate: let's say that the 64 internal queues contain 10 I/O request seach, and quantum is set to
8. In the first cycle, the cfq scheduler will take one request from each of the first 8 internal queues.
Those 8 requests are moved to the dispatch queue. In the next cycle (given that there are 8 free slots
in the dispatch queue) the cfq scheduler will take one request from each of the next batches of 8
internal queues.

這裡不得不提下io prority,我們可以用ionice來指定io priority,其中有三種class: idle(3), best effort(2), real time(1),具體用法man ionice

real time 和 best effort 內部都有 0-7 一共8個優先順序,對於real time而言,由於優先順序高,有可能會餓死其他進程,對於 best effort 而言,2.6.26之後的核心如不指定io priority,那就有io priority = cpu nice

關於io優先順序,有如下comments: I/O priorities are supported for reads and for synchronous (O_DIRECT, O_SYNC) writes.  I/O priorities are not supported for asynchronous  writes  because they are issued outside the context of the program dirtying the memory, and thus
program-specific priorities do not apply

言歸正傳,開始分析cfq的代碼

struct cfq_io_context

cfq_io_context可以理解為io_context的子類,代表一個task_struct在cfq裡的view,可以看到裡面有兩個cfq_queue結構的數組,cfq_queue[0]表示進程非同步io請求對應的cfq_queue隊列,cfq_queue[1]表示進程同步io請求對應的cfq_queue隊列

struct cfq_queue

cfq_queue是個進程相關的資料結構,會和一個cfq_io_context關聯,sort_list 是這個queue裡面 pending requests 構成的紅/黑樹狀結構,而 fifo 則是這個sort_list裡面的pending requests形成的fifo鏈表,這個設計有點類似deadline io scheduler。

對於某個進程而言,其io class(rt, be, idle), io priority, io type(sync, async), 進程所屬的cgroup 隨時都會變化,因此cfq_queue的成員也會跟著變化

struct cfq_rb_root* service_tree

struct rb_node rb_node

其中service_tree指向cfq_data對應的cfq_rb_tree,下面可以看到每個cfq_group對應7個cfq_rb_root為頭結點的service_tree,代表了不同的io class, io type;而rb_node則是這個紅/黑樹狀結構上的結點

struct rb_node* p_node

struct rb_root* p_root

cfq_data有個成員prio_trees,代表了8個紅/黑樹狀結構,每個cfq_queue都根據其io priority對應一個prio_trees成員,ioprio, ioprio_class記錄了這些資訊

struct cfq_group* cfqg

cfqg記錄了cfq_queue對應的cgroup

struct cfq_data

這是和塊裝置隊列相關的一個資料結構。cfq_data的指標是作為一個blkio_cgroup的雜湊表的key,而對應的value則是cfq_group;同時也是io_context的radix_tree的一個key,對應的value是cfq_io_context,這樣cfq_data,blkio_group和cfq_io_context就一一對應起來了。

cfq_data還有全域性的成員,專門針對非同步io的

struct cfq_queue* async_cfqq[2][IOPRIO_BE_NR]

struct cfq_queue* async_idle_cfqq

其中async_cfqq代表了RT/BE各8個優先順序的cfq_queue隊列,async_idle_cfqq代表了idle的cfq_queue隊列。

關於非同步同步的請求,有一種說法是,只有page cache write back(pdflush)線程的請求才是核心唯一的非同步請求,其他不論使用者態是同步還是非同步,不論是libaio還是核心native aio,到了調度隊列之後都是同步請求??

struct hlist_head cfqg_list 

指向該block device上掛載的所有cgroup,對應的hlist_node可以在struct cfq_group的cfqd_node成員中擷取

struct rb_root prio_trees[CFQ_PRIO_LISTS]

prio_trees代表了8個紅/黑樹狀結構,從priority 0 - 7

struct cfq_rb_root grp_service_tree

struct cfq_group root_cgroup

grp_service_tree為cfq_data所對應的block device上所有的cfq_group構成的紅/黑樹狀結構的樹根,可以看到cfq_group的成員rb_node就是這個紅/黑樹狀結構(cfq_rb_root)的結點

root_cgroup為所有cgroup之中的根cgroup

cfq_group

這是per cgroup對應的資料結構,成員rb_node指向了其在cgroup紅/黑樹狀結構中的結點

vdisktime

cfq_group->vdisktime可以理解為一個虛擬磁碟時間

在cfq_group_served函數中,當某個cfq_group被服務完之後,需要更新cfq_group->vdisktime,之後放回到service tree中。cfqg->vdisktime += cfq_scale_slice(charge, cfqg)是更新vdisktime的函數,其中charge是使用掉的time slice時間,cfq_scale_slice實現如下:

static inline u64 cfq_scale_slice(unsigned long delta, struct cfq_group *cfqg)
{
u64 d = delta << CFQ_SERVICE_SHIFT;
d = d * BLKIO_WEIGHT_DEFAULT;
do_div(d, cfqg->weight);
return d;
}

簡單的說,weight越大的cfq_group,在消耗了同樣的time slice之後,cfq_scle_slice返回的值相對較小,由於service tree每次都選擇紅/黑樹狀結構中vdisktime最小的cfq_group來服務,這就保證weight值大的cfq_group有更大幾率會被再次選擇服務

service tree會儲存一個紅/黑樹狀結構中當前最小的vdisktime值,存在min_vdisktime中

關於busy_queues_avg的解釋如下:

* Per group busy queues average. Useful for workload slice calc. We
* create the array for each prio class but at run time it is used
* only for RT and BE class and slot for IDLE class remains unused.
* This is primarily done to avoid confusion and a gcc warning.

struct cfq_rb_root service_trees[2][3]

struct cfq_rb_root service_tree_idle

這兩個成員在cgroup的patch出現之後從cfq_data被移到了新的cfq_group結構體中(2.6.33核心),我們可以看下代碼裡的注釋如下:

* rr lists of queues with requests. We maintain service trees for
* RT and BE classes. These trees are subdivided in subclasses
* of SYNC, SYNC_NOIDLE and ASYNC based on workload type. For IDLE
* class there is no subclassification and all the cfq queues go on
* a single tree service_tree_idle.
* Counts are embedded in the cfq_rb_root

這裡有一個我一直不明白的地方,service_trees已經是cfq_queue的紅/黑樹狀結構根,首先分為BE, RT兩大類(IDLE的全部在service_tree_idle裡),然後又分為SYNC, SYNC_IDLE, ASYNC這三類,這是很奇怪的地方,因為service_trees本身應該都是NO IDLE的分類了,為什麼又來個SYNC_IDLE呢

struct hlist_node cfqd_node

cfqd_node是一個雜湊項,其雜湊表頭hlist_head為cfq_data->cfqg_list,這個cfq_data->cfqg_list的雜湊表代表了這個block device上所有的cfq_group

struct blkio_group blkcg 

通過blkio_group可以找到對應的blkio_cgroup的結構,關於blkio_cgroup請參考之前關於cgroup的文章。注意這裡有兩個相似的資料結構:blkio_cgroup和blkio_group,那麼這兩個資料結構有什麼區別呢?我的猜測是,blkio_cgroup對應一個cgroup,可以從blkio_cgroup.css這個cgroup_subsys_state得到對應的cgroup結構。而blkio_group結構則是blkio_cgroup.blkg_list這個雜湊表的結點,通過blkio_group.blkcg_node相關聯。

注意到blkio_group裡有一個成員dev,據此猜測blkio_group是cgroup在不同塊裝置上應用的資料結構,e.g. 有四個進程ABCD,其中AB在sda上讀寫,CD在sdb上讀寫,這樣就會有兩個blkio_group結構,分別對應sda和sdb,這也和cfq_group對應起來,每個cfq_group對應一個 per cgroup per device的資料結構,因此有一個blkio_group的成員變數把兩者關聯起來

這些資料結構之間的關係是怎樣的?我大概歸納了一下,不一定準確:

cfq_group正如代碼中的解釋那樣,是一個 /* This is per cgroup per device grouping structure */ 的結構,比如某個cgroup有兩個tasks,一個對/dev/sda寫,一個對/dev/sdb寫,那麼就對應著兩個cfq_group結構,但如果兩個tasks都是寫/dev/sda,那麼這兩個進程都在同一個cfq_group中

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.