将實體打散成不可再分的微粒,這樣就可以使設計靈活化,最大限度的減少資料備援。以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