天天看點

一個資源管理系統的設計--解析linux的cgroup實作

将實體打散成不可再分的微粒,這樣就可以使設計靈活化,最大限度的減少資料備援。以CRM系統為例,雖然管理是基于一組控制元素而不是一個控制元素的,設計的時候還是以一個控制元素為基礎。

     linux的cgroup系統可謂是一個典範,它輕量地實作了諸如solaris的“容器”的概念,也許也是對linux本身“命名空間”的一種沖擊。它是分層的,也可以說是樹形的結構,一個“控制組”擁有一個ROOT,每一個ROOT控制一組元素,在這個ROOT當中可以建立很多的“組”(cgroup),每一個組還可以建立下級的組...。每建立一個cgroup必須确定一組它關心的cgroup_subsys,比如cpuset,memory,ns等,怎麼确定呢?這是通過檔案系統的mount實作的,當你執行:

mount -t cgroup cgroup -o cpuset memory my

的時候,你就建立一個ROOT,這個ROOT包含所有的程序,然後你可以在my目錄中執行mkdir group1 group2,這樣就建立了兩個cgroup,實際上,當你mount的時候,系統就建立了一個虛拟的組group-root,如果你執行cat my/tasks,你會發現它包含了所有的程序,如果你執行cat my/group1/tasks,你會發現它是空的,因為還沒有任何的程序被加入進去。現在執行echo 761>my/group1/tasks,那麼pid為761的程序将被加入到group1,通過修改group1目錄下的檔案就可以對這個group1中目前tasks檔案中包含的所有的程序進行控制了。這一切是如何實作的?實際上linux核心代碼的cgroup子系統實作了類似資料庫的結構,包括表結構和查詢引擎,通過閱讀代碼可以看出,每一個程序task包含一個css_set類型的字段:

struct css_set {

    struct kref ref;

    struct hlist_node hlist;

    struct list_head tasks;      //解決備援,包含所有使用這個set的程序

    struct list_head cg_links;   //解決備援,包含所有參與管理這個set的cgroup。

    struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];

};

既然它被一組subsys控制,為何不直接将每個subsys本身包含在task中呢?這是為了解決資料備援的問題,因為其他的程序也可以受這些subsys控制。

     有必要說一下上面的css_set結構中的cg_links字段,它包含了所有的參與管理這個set的cgroup,為何會這樣呢?難道一個set不是由一個cgroup管理的嗎?不是的,要知道管理是基于“一組”subsys的,而這一組并不一定是全部的編譯進核心也就是核心支援的subsys。比如你分别用-o參數cpu,memory...mount了5個cgroup檔案系統,那麼一個程序關聯的css_set就會由5個cgroup管理,每一個mount的ROOT會管理一個,這個ROOT會用檔案系統的方式管理程序分别屬于該ROOT的哪個cgroup。

     下面看一下cgroup_subsys_state,這個結構可以看作靜态cgroup_subsys結構的動态執行個體,靜态的cgroup_subsys中包含了一些通用的方法,而動态的cgroup_subsys_state則僅僅是一個父類,具體的資料和額外的方法通過繼承它來實作,比如:

struct mem_cgroup {

    struct cgroup_subsys_state css;

    struct res_counter res;

    struct mem_cgroup_lru_info info;

    int    prev_priority;    /* for recording reclaim priority */

    struct mem_cgroup_stat stat;

任何時候,隻要你得到了一個cgroup_subsys_state,并且根據其subsys_id确認它是關于memory的,那麼就可以通過:

static inline struct cgroup_subsys_state *task_subsys_state(

    struct task_struct *task, int subsys_id)

{

    return rcu_dereference(task->cgroups->subsys[subsys_id]);

}

container_of(task_subsys_state(p, mem_cgroup_subsys_id), struct mem_cgroup, css);

來取得這個可被稱為子類執行個體的mem_cgroup,接下來就可以操作它的資料了。終于可以看一下cgroup_subsys_state了:

struct cgroup_subsys_state {

    struct cgroup *cgroup;

    atomic_t refcnt;

    unsigned long flags;

這個結構很簡單,cgroup是它綁定的一個cgroup執行個體,從名稱上也可以看出cgroup_subsys_state結構是動态的,它表示程序的subsys的state,由于它是被cgroup管理的,是以它也隻能有一個cgroup與其綁定。

     接下來看一下cgroup結構,這好像是一個重量級的結構,其實不然,它僅僅起到一個粘合的作用,換句話說就是管理者:

struct cgroup {

    unsigned long flags;         

    atomic_t count;

    struct list_head sibling;     //此和以下幾個實作了樹型結構

    struct list_head children;     

    struct cgroup *parent;     

    struct dentry *dentry;          

    struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; //這裡僅包含對應ROOT相關的subsys

    struct cgroupfs_root *root;     //對應的那個ROOT

    struct cgroup *top_cgroup;

    struct list_head css_sets;    //包含所有的它參與管理的css_set

    struct list_head release_list;

前面提到過,程序要顯式加入一個ROOT的cgroup,在加入的時候可能會為程序綁定一個新的css_set(必須保證css_set的subsys數組完全相同才能重用,如果之前沒有這樣的css_set建立,隻好建立立一個),隻要綁定了一個新的css_set,這個set就要加入到cgroup的css_sets連結清單中,最簡單的可以在css_set中添加一個字段用于此目的,與此同時cgroup中也要增加一個list_head結構用來連結css_set的cg_links字段,這樣做為何不好呢?它增加了兩個資料結構的耦合性,同時也增加了資料的備援性,因為一個cgroup的ROOT負責一組subsys,一個程序也是和一組subsys關聯,是以隻需要一個程序的一組subsys中被同一個ROOT管理的第一個加入到cgroup連結清單中就可以表示一個程序受到了這個cgroup的管理,比如程序p1加入group1,該group1的ROOT管理cpu和memory,那麼其css_set的subsys數組中隻需要cpu_id的這個subsys加入cgroup的css_sets連結清單就可以了,為了代碼的簡單,是以引入了一個中間結構,那就是cg_cgroup_link:

struct cg_cgroup_link {

    struct list_head cgrp_link_list;    //代表一個css_set加入到cgroup

    struct list_head cg_link_list;        //代表一個cgroup加入到css_set

    struct css_set *cg;            //指回css_set

    ...  //後續的核心還要指回cgroup,這裡的核心是2.6.26

在此必須說一下為何要有cg_cgroup_link這個結構體。如果在css_set中和cgroup中直接加傳入連結表元素是解決不了多對多問題的,比如所有參與管理一個css_set的cgroup都将其連結清單元素加入 css_set的連結清單,反過來css_set的連結清單元素也應該加入一個cgroup的連結清單,代表它是該cgroup管理的css_set之一,現在問題來了,前面說過一個css_set可以屬于很多ROOT,那麼它到底加入哪個cgroup的連結清單呢?畢竟css_set和cgroup之間是如此單項一對多的關系耦合,是以解除耦合的辦法就是設計一個中間結構,那就是cg_cgroup_link。

     很多時候,很多人在網上寫了一大堆關于分析“linux核心”的文章,很多文章都是僅僅分析代碼流程,但是很少有文章能說明為何這麼做(除非文章的作者着手送出一個更新檔或者其它...)。其實linux核心就是一個資料庫設計的教程,它不但展示了表結構,而且還有查詢引擎,故而linux核心絕對是絕妙的哦!比如在input子系統中,input_handle這個結構體也是和cg_cgroup_link意義一樣的,也是為了解決多對多的問題而設定的,還有一個明顯的例子,那就是linux核心中的總線驅動架構。

     由此看來,學習linux核心可以學到兩大當今時髦的東西,一個就是OO,另一個就是資料庫的設計,千萬不要以為linux核心僅僅是底層的東西,搞應用的人不用學習,其實各個領域是相通的,我相信,當一個頂級的文學家聽說了廣義相對論的時候,他也一定會提出一些自己的看法的。我經常看曆史著作,那些作者們看起來對任何領域都很感興趣...

附:看一下cgroup的靜态資料結構們吧

首先看一下一個靜态的超類,那就是cgroup_subsys,它包含了一系列的接口,但是沒有實作!

struct cgroup_subsys {

    struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss,

                          struct cgroup *cgrp);

    ...//類似的接口

    int subsys_id;

    int active;

    int disabled;

    int early_init;

#define MAX_CGROUP_TYPE_NAMELEN 32

    const char *name;

    struct cgroupfs_root *root;

    struct list_head sibling; //用于挂載一系列的state

    void *private;            //用于擴充

每一個挂載(mount)的cgroup檔案系統都有一個cgroupfs_root執行個體:

struct cgroupfs_root {

    struct super_block *sb;

    unsigned long subsys_bits;

    unsigned long actual_subsys_bits;

    struct list_head subsys_list;      //本ROOT關注的subsys

    struct cgroup top_cgroup;

    int number_of_cgroups;

    struct list_head root_list;

    char release_agent_path[PATH_MAX];

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1271176