1. 程式人生 > >CGroup 介紹、應用例項及原理描述

CGroup 介紹、應用例項及原理描述

在 IBM Bluemix 雲平臺上開發並部署您的下一個應用。

CGroup 介紹

CGroup 是 Control Groups 的縮寫,是 Linux 核心提供的一種可以限制、記錄、隔離程序組 (process groups) 所使用的物力資源 (如 cpu memory i/o 等等) 的機制。2007 年進入 Linux 2.6.24 核心,CGroups 不是全新創造的,它將程序管理從 cpuset 中剝離出來,作者是 Google 的 Paul Menage。CGroups 也是 LXC 為實現虛擬化所使用的資源管理手段。

CGroup 功能及組成

CGroup 是將任意程序進行分組化管理的 Linux 核心功能。CGroup 本身是提供將程序進行分組化管理的功能和介面的基礎結構,I/O 或記憶體的分配控制等具體的資源管理功能是通過這個功能來實現的。這些具體的資源管理功能稱為 CGroup 子系統或控制器。CGroup 子系統有控制記憶體的 Memory 控制器、控制程序排程的 CPU 控制器等。執行中的核心可以使用的 Cgroup 子系統由/proc/cgroup 來確認。

CGroup 提供了一個 CGroup 虛擬檔案系統,作為進行分組管理和各子系統設定的使用者介面。要使用 CGroup,必須掛載 CGroup 檔案系統。這時通過掛載選項指定使用哪個子系統。

CGroup 支援的檔案種類

表 1. CGroup 支援的檔案種類
檔名 R/W 用途

Release_agent

RW

刪除分組時執行的命令,這個檔案只存在於根分組

Notify_on_release

RW

設定是否執行 release_agent。為 1 時執行

Tasks

RW

屬於分組的執行緒 TID 列表

Cgroup.procs

R

屬於分組的程序 PID 列表。僅包括多執行緒程序的執行緒 leader 的 TID,這點與 tasks 不同

Cgroup.event_control

RW

監視狀態變化和分組刪除事件的配置檔案

CGroup 相關概念解釋

  1. 任務(task)。在 cgroups 中,任務就是系統的一個程序;

  2. 控制族群(control group)。控制族群就是一組按照某種標準劃分的程序。Cgroups 中的資源控制都是以控制族群為單位實現。一個程序可以加入到某個控制族群,也從一個程序組遷移到另一個控制族群。一個程序組的程序可以使用 cgroups 以控制族群為單位分配的資源,同時受到 cgroups 以控制族群為單位設定的限制;

  3. 層級(hierarchy)。控制族群可以組織成 hierarchical 的形式,既一顆控制族群樹。控制族群樹上的子節點控制族群是父節點控制族群的孩子,繼承父控制族群的特定的屬性;

  4. 子系統(subsystem)。一個子系統就是一個資源控制器,比如 cpu 子系統就是控制 cpu 時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起作用,一個子系統附加到某個層級以後,這個層級上的所有控制族群都受到這個子系統的控制。

相互關係

  1. 每次在系統中建立新層級時,該系統中的所有任務都是那個層級的預設 cgroup(我們稱之為 root cgroup,此 cgroup 在建立層級時自動建立,後面在該層級中建立的 cgroup 都是此 cgroup 的後代)的初始成員;

  2. 一個子系統最多隻能附加到一個層級;

  3. 一個層級可以附加多個子系統;

  4. 一個任務可以是多個 cgroup 的成員,但是這些 cgroup 必須在不同的層級;

  5. 系統中的程序(任務)建立子程序(任務)時,該子任務自動成為其父程序所在 cgroup 的成員。然後可根據需要將該子任務移動到不同的 cgroup 中,但開始時它總是繼承其父任務的 cgroup。

圖 1. CGroup 層級圖
圖 1. CGroup 層級圖

圖 1 所示的 CGroup 層級關係顯示,CPU 和 Memory 兩個子系統有自己獨立的層級系統,而又通過 Task Group 取得關聯關係。

CGroup 特點

  1. 在 cgroups 中,任務就是系統的一個程序。

  2. 控制族群(control group)。控制族群就是一組按照某種標準劃分的程序。Cgroups 中的資源控制都是以控制族群為單位實現。一個程序可以加入到某個控制族群,也從一個程序組遷移到另一個控制族群。一個程序組的程序可以使用 cgroups 以控制族群為單位分配的資源,同時受到 cgroups 以控制族群為單位設定的限制。

  3. 層級(hierarchy)。控制族群可以組織成 hierarchical 的形式,既一顆控制族群樹。控制族群樹上的子節點控制族群是父節點控制族群的孩子,繼承父控制族群的特定的屬性。

  4. 子系統(subsytem)。一個子系統就是一個資源控制器,比如 cpu 子系統就是控制 cpu 時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起作用,一個子系統附加到某個層級以後,這個層級上的所有控制族群都受到這個子系統的控制。

CGroup 應用架構

圖 2. CGroup 典型應用架構圖
圖 2. CGroup 典型應用架構圖

如圖 2 所示,CGroup 技術可以被用來在作業系統底層限制物理資源,起到 Container 的作用。圖中每一個 JVM 程序對應一個 Container Cgroup 層級,通過 CGroup 提供的各類子系統,可以對每一個 JVM 程序對應的執行緒級別進行物理限制,這些限制包括 CPU、記憶體等等許多種類的資源。下一部分會具體對應用程式進行 CPU 資源隔離進行演示。

CGroup 部署及應用例項

講解 CGroup 設計原理前,我們先來做一個簡單的實驗。實驗基於 Linux Centosv6.564 位版本,JDK1.7。實驗目的是執行一個佔用 CPU 的 Java 程式,如果不用 CGroup 物理隔離 CPU 核,那程式會由作業系統層級自動挑選 CPU 核來執行程式。由於作業系統層面採用的是時間片輪詢方式隨機挑選 CPU 核作為執行容器,所以會在本機器上 24 個 CPU 核上隨機執行。如果採用 CGroup 進行物理隔離,我們可以選擇某些 CPU 核作為指定執行載體。

清單 1.Java 程式程式碼
//開啟 4 個使用者執行緒,其中 1 個執行緒大量佔用 CPU 資源,其他 3 個執行緒則處於空閒狀態
public class HoldCPUMain {
 public static class HoldCPUTask implements Runnable{

@Override
public void run() {
// TODO Auto-generated method stub
while(true){
double a = Math.random()*Math.random();//佔用 CPU
System.out.println(a);
}
}
 
 }
 
 public static class LazyTask implements Runnable{

@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//空閒執行緒
}
}
 
 }
 
 
 public static void main(String[] args){
 for(int i=0;i<10;i++){
 new Thread(new HoldCPUTask()).start();
 }
 }
}

清單 1 程式會啟動 10 個執行緒,這 10 個執行緒都在做佔用 CPU 的計算工作,它們可能會執行在 1 個 CPU 核上,也可能執行在多個核上,由作業系統決定。我們稍後會在 Linux 機器上通過命令在後臺執行清單 1 程式。本實驗需要對 CPU 資源進行限制,所以我們在 cpu_and_set 子系統上建立自己的層級“zhoumingyao”。

清單 2. 建立層級
[[email protected] cpu_and_set]# ls -rlt
總用量 0
-rw-r--r-- 1 root root 0 3 月 21 17:21 release_agent
-rw-r--r-- 1 root root 0 3 月 21 17:21 notify_on_release
-r--r--r-- 1 root root 0 3 月 21 17:21 cpu.stat
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpu.shares
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.mems
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.memory_pressure_enabled
-r--r--r-- 1 root root 0 3 月 21 17:21 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.memory_migrate
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.cpus
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpu.rt_period_us
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 3 月 21 17:21 cpu.cfs_period_us
-r--r--r-- 1 root root 0 3 月 21 17:21 cgroup.procs
drwxr-xr-x 2 root root 0 3 月 21 17:22 test
drwxr-xr-x 2 root root 0 3 月 23 16:36 test1
-rw-r--r-- 1 root root 0 3 月 25 19:23 tasks
drwxr-xr-x 2 root root 0 3 月 31 19:32 single
drwxr-xr-x 2 root root 0 3 月 31 19:59 single1
drwxr-xr-x 2 root root 0 3 月 31 19:59 single2
drwxr-xr-x 2 root root 0 3 月 31 19:59 single3
drwxr-xr-x 3 root root 0 4 月 3 17:34 aaaa
[[email protected] cpu_and_set]# mkdir zhoumingyao
[[email protected] cpu_and_set]# cd zhoumingyao
[[email protected] zhoumingyao]# ls -rlt
總用量 0
-rw-r--r-- 1 root root 0 4 月 30 14:03 tasks
-rw-r--r-- 1 root root 0 4 月 30 14:03 notify_on_release
-r--r--r-- 1 root root 0 4 月 30 14:03 cpu.stat
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpu.shares
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.mems
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.memory_spread_page
-r--r--r-- 1 root root 0 4 月 30 14:03 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.memory_migrate
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.cpus
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpu.rt_period_us
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 4 月 30 14:03 cpu.cfs_period_us
-r--r--r-- 1 root root 0 4 月 30 14:03 cgroup.procs

通過 mkdir 命令新建資料夾 zhoumingyao,由於已經預先載入 cpu_and_set 子系統成功,所以當資料夾建立完畢的同時,cpu_and_set 子系統對應的資料夾也會自動建立。

執行 Java 程式前,我們需要確認 cpu_and_set 子系統安裝的目錄,如清單 3 所示。

清單 3. 確認目錄
[[email protected] zhoumingyao]# lscgroup 
cpuacct:/
devices:/
freezer:/
net_cls:/
blkio:/
memory:/
memory:/test2
cpuset,cpu:/
cpuset,cpu:/zhoumingyao
cpuset,cpu:/aaaa
cpuset,cpu:/aaaa/bbbb
cpuset,cpu:/single3
cpuset,cpu:/single2
cpuset,cpu:/single1
cpuset,cpu:/single
cpuset,cpu:/test1
cpuset,cpu:/test

輸出顯示 cpuset_cpu 的目錄是 cpuset,cpu:/zhoumingyao,由於本實驗所採用的 Java 程式是多執行緒程式,所以需要使用 cgexec 命令來幫助啟動,而不能如網路上有些材料所述,採用 java –jar 命令啟動後,將 pid 程序號填入 tasks 檔案即可的錯誤方式。清單 4 即採用 cgexec 命令啟動 java 程式,需要使用到清單 3 定位到的 cpuset_cpu 目錄地址。

清單 4. 執行 Java 程式

[[email protected] zhoumingyao]# cgexec -g cpuset,cpu:/zhoumingyao java -jar test.jars

我們在 cpuset.cpus 檔案中設定需要限制只有 0-10 這 11 個 CPU 核可以被用來執行上述清單 4 啟動的 Java 多執行緒程式。當然 CGroup 還可以限制具體每個核的使用百分比,這裡不再做過多的描述,請讀者自行翻閱 CGroup 官方材料。

清單 5.cpu 核限制
[[email protected] zhoumingyao]# cat cpuset.cpus
0-10

接下來,通過 TOP 命令獲得清單 4 啟動的 Java 程式的所有相關執行緒 ID,將這些 ID 寫入到 Tasks 檔案。

清單 6. 設定執行緒 ID
[[email protected] zhoumingyao]# cat tasks
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2714
2715
2716
2718

全部設定完畢後,我們可以通過 TOP 命令檢視具體的每一顆 CPU 核上的執行情況,發現只有 0-10 這 11 顆 CPU 核上有計算資源被呼叫,可以進一步通過 TOP 命令確認全部都是清單 4 所啟動的 Java 多執行緒程式的執行緒。

清單 7. 執行結果
top - 14:43:24 up 44 days, 59 min, 6 users, load average: 0.47, 0.40, 0.33
Tasks: 715 total, 1 running, 714 sleeping, 0 stopped, 0 zombie
Cpu0 : 0.7%us, 0.3%sy, 0.0%ni, 99.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu1 : 1.0%us, 0.7%sy, 0.0%ni, 98.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu2 : 0.3%us, 0.3%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu3 : 1.0%us, 1.6%sy, 0.0%ni, 97.5%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu4 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu5 : 1.3%us, 1.9%sy, 0.0%ni, 96.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu6 : 3.8%us, 5.4%sy, 0.0%ni, 90.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu7 : 7.7%us, 9.9%sy, 0.0%ni, 82.4%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu8 : 4.8%us, 6.1%sy, 0.0%ni, 89.1%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu9 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu10 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu11 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu12 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu13 : 0.0%us, 0.0%sy, 0.0%ni, 72.8%id, 0.0%wa, 0.0%hi, 4.3%si, 0.0%st
Cpu14 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu15 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu16 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu17 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu18 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu19 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu20 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu21 : 0.3%us, 0.3%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu22 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu23 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 32829064k total, 5695012k used, 27134052k free, 533516k buffers
Swap: 24777720k total, 0k used, 24777720k free, 3326940k cached

總體上來說,CGroup 的使用方式較為簡單,目前主要的問題是網路上已有的中文材料缺少詳細的配置步驟,一旦讀者通過反覆實驗,掌握了配置方式,使用上應該不會有大的問題。

Cgroup 設計原理分析

CGroups 的原始碼較為清晰,我們可以從程序的角度出發來剖析 cgroups 相關資料結構之間的關係。在 Linux 中,管理程序的資料結構是 task_struct,其中與 cgroups 有關的程式碼如清單 8 所示:

清單 8.task_struct 程式碼
#ifdef CONFIG_CGROUPS 
/* Control Group info protected by css_set_lock */ 
struct css_set *cgroups; 
/* cg_list protected by css_set_lock and tsk->alloc_lock */ 
struct list_head cg_list; 
#endif

其中 cgroups 指標指向了一個 css_set 結構,而 css_set 儲存了與程序有關的 cgroups 資訊。cg_list 是一個嵌入的 list_head 結構,用於將連到同一個 css_set 的程序組織成一個連結串列。下面我們來看 css_set 的結構,程式碼如清單 9 所示:

清單 9.css_set 程式碼
struct css_set { 
atomic_t refcount;
struct hlist_node hlist; 
struct list_head tasks; 
struct list_head cg_links; 
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; 
struct rcu_head rcu_head; 
};

其中 refcount 是該 css_set 的引用數,因為一個 css_set 可以被多個程序公用,只要這些程序的 cgroups 資訊相同,比如:在所有已建立的層級裡面都在同一個 cgroup 裡的程序。hlist 是嵌入的 hlist_node,用於把所有 css_set 組織成一個 hash 表,這樣核心可以快速查詢特定的 css_set。tasks 指向所有連到此 css_set 的程序連成的連結串列。cg_links 指向一個由 struct_cg_cgroup_link 連成的連結串列。

Subsys 是一個指標陣列,儲存一組指向 cgroup_subsys_state 的指標。一個 cgroup_subsys_state 就是程序與一個特定子系統相關的資訊。通過這個指標陣列,程序就可以獲得相應的 cgroups 控制資訊了。cgroup_subsys_state 結構如清單 10 所示:

清單 10.cgroup_subsys_state 程式碼
struct cgroup_subsys_state { 
struct cgroup *cgroup; 
atomic_t refcnt; 
unsigned long flags; 
struct css_id *id; 
};

cgroup 指標指向了一個 cgroup 結構,也就是程序屬於的 cgroup。程序受到子系統的控制,實際上是通過加入到特定的 cgroup 實現的,因為 cgroup 在特定的層級上,而子系統又是附和到上面的。通過以上三個結構,程序就可以和 cgroup 連線起來了:task_struct->css_set->cgroup_subsys_state->cgroup。cgroup 結構如清單 11 所示:

清單 11.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]; 
struct cgroupfs_root *root;
struct cgroup *top_cgroup; 
struct list_head css_sets; 
struct list_head release_list; 
struct list_head pidlists;
struct mutex pidlist_mutex; 
struct rcu_head rcu_head; 
struct list_head event_list; 
spinlock_t event_list_lock; 
};

sibling,children 和 parent 三個嵌入的 list_head 負責將統一層級的 cgroup 連線成一棵 cgroup 樹。

subsys 是一個指標陣列,儲存一組指向 cgroup_subsys_state 的指標。這組指標指向了此 cgroup 跟各個子系統相關的資訊,這個跟 css_set 中的道理是一樣的。

root 指向了一個 cgroupfs_root 的結構,就是 cgroup 所在的層級對應的結構體。這樣一來,之前談到的幾個 cgroups 概念就全部聯絡起來了。

top_cgroup 指向了所在層級的根 cgroup,也就是建立層級時自動建立的那個 cgroup。

css_set 指向一個由 struct_cg_cgroup_link 連成的連結串列,跟 css_set 中 cg_links 一樣。

下面分析一個 css_set 和 cgroup 之間的關係,cg_cgroup_link 的結構如清單 12 所示:

清單 12.cg_cgroup_link 程式碼
struct cg_cgroup_link { 
struct list_head cgrp_link_list; 
struct cgroup *cgrp; 
struct list_head cg_link_list; 
struct css_set *cg; };

cgrp_link_list 連入到 cgrouo->css_set 指向的連結串列,cgrp 則指向此 cg_cgroup_link 相關的 cgroup。

cg_link_list 則連入到 css_set->cg_lonks 指向的連結串列,cg 則指向此 cg_cgroup_link 相關的 css_set。

cgroup 和 css_set 是一個多對多的關係,必須新增一箇中間結構來將兩者聯絡起來,這就是 cg_cgroup_link 的作用。cg_cgroup_link 中的 cgrp 和 cg 就是此結構提的聯合主鍵,而 cgrp_link_list 和 cg_link_list 分別連入到 cgroup 和 css_set 相應的連結串列,使得能從 cgroup 或 css_set 都可以進行遍歷查詢。

那為什麼 cgroup 和 css_set 是多對多的關係呢?

一個程序對應一個 css_set,一個 css_set 儲存了一組程序 (有可能被多個程序共享,所以是一組) 跟各個子系統相關的資訊,但是這些資訊由可能不是從一個 cgroup 那裡獲得的,因為一個程序可以同時屬於幾個 cgroup,只要這些 cgroup 不在同一個層級。舉個例子:我們建立一個層級 A,A 上面附加了 cpu 和 memory 兩個子系統,程序 B 屬於 A 的根 cgroup;然後我們再建立一個層級 C,C 上面附加了 ns 和 blkio 兩個子系統,程序 B 同樣屬於 C 的根 cgroup;那麼程序 B 對應的 cpu 和 memory 的資訊是從 A 的根 cgroup 獲得的,ns 和 blkio 資訊則是從 C 的根 cgroup 獲得的。因此,一個 css_set 儲存的 cgroup_subsys_state 可以對應多個 cgroup。另一方面,cgroup 也儲存了一組 cgroup_subsys_state,這一組 cgroup_subsys_state 則是 cgroup 從所在的層級附加的子系統獲得的。一個 cgroup 中可以有多個程序,而這些程序的 css_set 不一定都相同,因為有些程序可能還加入了其他 cgroup。但是同一個 cgroup 中的程序與該 cgroup 關聯的 cgroup_subsys_state 都受到該 cgroup 的管理 (cgroups 中程序控制是以 cgroup 為單位的) 的,所以一個 cgroup 也可以對應多個 css_set。

從前面的分析,我們可以看出從 task 到 cgroup 是很容易定位的,但是從 cgroup 獲取此 cgroup 的所有的 task 就必須通過這個結構了。每個程序都回指向一個 css_set,而與這個 css_set 關聯的所有程序都會鏈入到 css_set->tasks 連結串列,而 cgroup 又通過一箇中間結構 cg_cgroup_link 來尋找所有與之關聯的所有 css_set,從而可以得到與 cgroup 關聯的所有程序。最後,我們看一下層級和子系統對應的結構體。層級對應的結構體是 cgroupfs_root 如清單 13 所示:

清單 13.cgroupfs_root 程式碼
struct cgroupfs_root { 
struct super_block *sb; 
unsigned long subsys_bits; 
int hierarchy_id;
unsigned long actual_subsys_bits; 
struct list_head subsys_list; 
struct cgroup top_cgroup; 
int number_of_cgroups; 
struct list_head root_list; 
unsigned long flags; 
char release_agent_path[PATH_MAX]; 
char name[MAX_CGROUP_ROOT_NAMELEN]; 
};

sb 指向該層級關聯的檔案系統資料塊。subsys_bits 和 actual_subsys_bits 分別指向將要附加到層級的子系統和現在實際附加到層級的子系統,在子系統附加到層級時使用。hierarchy_id 是該層級唯一的 id。top_cgroup 指向該層級的根 cgroup。number_of_cgroups 記錄該層級 cgroup 的個數。root_list 是一個嵌入的 list_head,用於將系統所有的層級連成連結串列。子系統對應的結構體是 cgroup_subsys,程式碼如清單 14 所示。

清單 14. cgroup_subsys 程式碼
struct cgroup_subsys { 
struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss, 
struct cgroup *cgrp); 
int (*pre_destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp); 
void (*destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp); 
int (*can_attach)(struct cgroup_subsys *ss,
 struct cgroup *cgrp, struct task_struct *tsk, bool threadgroup); 
void (*cancel_attach)(struct cgroup_subsys *ss, 
struct cgroup *cgrp, struct task_struct *tsk, bool threadgroup); 
void (*attach)(struct cgroup_subsys *ss, struct cgroup *cgrp, 
struct cgroup *old_cgrp, struct task_struct *tsk, bool threadgroup); 
void (*fork)(struct cgroup_subsys *ss, struct task_struct *task); 
void (*exit)(struct cgroup_subsys *ss, struct task_struct *task); 
int (*populate)(struct cgroup_subsys *ss, struct cgroup *cgrp); 
void (*post_clone)(struct cgroup_subsys *ss, struct cgroup *cgrp); 
void (*bind)(struct cgroup_subsys *ss, struct cgroup *root);
int subsys_id; 
int active; 
int disabled; 
int early_init; 
bool use_id; 
#define MAX_CGROUP_TYPE_NAMELEN 32 
const char *name; 
struct mutex hierarchy_mutex; 
struct lock_class_key subsys_key; 
struct cgroupfs_root *root; 
struct list_head sibling; 
struct idr idr; 
spinlock_t id_lock; 
struct module *module; 
};

cgroup_subsys 定義了一組操作,讓各個子系統根據各自的需要去實現。這個相當於 C++中抽象基類,然後各個特定的子系統對應 cgroup_subsys 則是實現了相應操作的子類。類似的思想還被用在了 cgroup_subsys_state 中,cgroup_subsys_state 並未定義控制資訊,而只是定義了各個子系統都需要的共同資訊,比如該 cgroup_subsys_state 從屬的 cgroup。然後各個子系統再根據各自的需要去定義自己的程序控制資訊結構體,最後在各自的結構體中將 cgroup_subsys_state 包含進去,這樣通過 Linux 核心的 container_of 等巨集就可以通過 cgroup_subsys_state 來獲取相應的結構體。

從基本層次順序定義上來看,由 task_struct、css_set、cgroup_subsys_state、cgroup、cg_cgroup_link、cgroupfs_root、cgroup_subsys 等結構體組成的 CGroup 可以基本從程序級別反應之間的響應關係。後續文章會針對檔案系統、各子系統做進一步的分析。

結束語

就象大多數開源技術一樣,CGroup 不是全新創造的,它將程序管理從 cpuset 中剝離出來。通過物理限制的方式為程序間資源控制提供了簡單的實現方式,為 Linux Container 技術、虛擬化技術的發展奠定了技術基礎,本文的目標是讓初學者可以通過自己動手的方式簡單地理解技術,將起步門檻放低。

相關推薦

Cgroup介紹應用例項原理描述

Cgroup介紹 CGroup 是 Control Groups 的縮寫,是 Linux 核心提供的一種可以限制、記錄、隔離程序組 (process groups) 所使用的物力資源 (如 cpu memory i/o 等等) 的機制。2007 年進入 Lin

CGroup 介紹應用例項原理描述

在 IBM Bluemix 雲平臺上開發並部署您的下一個應用。 CGroup 介紹 CGroup 是 Control Groups 的縮寫,是 Linux 核心提供的一種可以限制、記錄、隔離程序組 (process groups) 所使用的物力資源 (如 cpu memory i/o 等等) 的

<Golang>MD5SHA256等雜湊演算法介紹應用場景具體實現

版權宣告:本文為作者原創,如需轉載,請註明出處https://blog.csdn.net/weixin_42940826 前言 MD5和SHA256是非常常用的兩種單向雜湊函式,雖然MD5在2005年已經被中國密碼學家王小云攻破,但是曾經也是叱吒風雲的被大規模使用,現在

bash腳本之函數簡單介紹應用函數的簡單遞歸調用

bash腳本bash腳本編程--函數 在bash中,函數是由命令和語句結構構成的能夠實現特定功能的集合; 為什麽要在bash中引入函數? 在bash腳本編寫過程中有可能會出現重復且不做任何改變的代碼內容,如果這類內容全靠原始代碼書寫的話不易於排錯和優化;因此我們可以選擇將此類代碼封裝在函數中,在適當的場景中可

Kafka訊息佇列介紹環境搭建應用:C#實現消費者-生產者訂閱

一:kafka介紹 kafka(官網地址:http://kafka.apache.org)是一種高吞吐量的分散式釋出訂閱的訊息佇列系統,具有高效能和高吞吐率。 1.1 術語介紹 Broker Kafka叢集包含一個或多個伺服器,這種伺服器被稱為broker

簡單工廠模式--介紹應用程式碼

工廠模式是我們最常用的模式了,著名的Jive論壇 ,就大量使用了工廠模式,工廠模式在Java程式系統可以說是隨處可見。因為工廠模式就相當於建立例項物件的new,我們經常要根據類Class生成例項物件,如A a=new A() 工廠模式也是用來建立例項物件的,所以以後new時就要多個心眼,是否可以考慮使用工廠

TensorFlow儲存載入模型引數 | 原理描述踩坑經驗總結

寫在前面 我之前使用的LSTM計算單元是根據其前向傳播的計算公式手動實現的,這兩天想要和TensorFlow自帶的tf.nn.rnn_cell.BasicLSTMCell()比較一下,看看哪個訓練速度快一些。在使用tf.nn.rnn_cell.BasicLSTMCell()進行建模的時候,遇到了模型儲存、載入

JSONP 介紹應用

輸出 bsp inf htm xmlns php innerhtml add 介紹 ylbtech-JSON: 介紹、應用 JSONP(JSON with Padding)是 JSON 的一種“使用模式”,可以讓網頁從別

rsync文件同步工具介紹常用選項rsync通過ssh同步 (一)

20180514一、rsync工具介紹(文件同步工具)1?實現a目錄保存到b目錄下,但是a目錄的數據一直在更新。用cp命令很浪費時間。這時我們就要用到rsync命令了。它可以實現增量拷貝,也支持遠程同步。本地拷貝:rsync -av /etc/passwd /tmp/1.txt遠程拷貝:rsync -av

深入理解Lua的閉包一:概念應用和實現原理

觀點 數組 line Language 場景 test 詞法 nil 實參 本文首先通過具體的例子講解了Lua中閉包的概念,然後總結了閉包的應用場合,最後探討了Lua中閉包的實現原理。 閉包的概念 在Lua中,閉包(closure)是由一個函數和該函數會訪問到的

矩陣應用例項js實現矩陣轉置演算法

場景: 後端返回的是[[‘2015-1-1’,1,1],[‘2015-1-2’,1,2]]這樣的Json陣列,代表的意思是2015-1-1這個日期下新增的資料為1,減少的資料為1,2015-1-2這個日期,新增的資料為1,減少的資料為2,但是在統計圖表上要在x軸顯示時間,y軸顯示新增和減少的資料這時,就要把

[C++] GetCursorPos函式的使用方法應用例項

如果有小夥伴看不懂太多英語,還想知道GetCursorPos函式的用法,那麼往下看吧。 函式原型: BOOL GetCursorPos( LPPOINT lpPoint ); 引數: lpPoint:一個指向POINT(struct)的指標,返回游標位置。

redis學習筆記01 — 基本介紹安裝配置常用命令

redis——NoSQL的一種 為了解決高併發、高可用、高擴充套件、大資料儲存等一系列問題而產生的資料庫解決方案,就是NoSQL NoSQL,非關係型資料庫,全名:Not Only Sql,它不能代替關係型資料庫,只能作為關係型資料庫的一個良好補充。 redis 簡介 是使用

HBase概念基本架構原理

概述 HBase是一個構建在HDFS上的分散式列儲存系統;HBase是基於Google BigTable模型開發的,典型的key/value系統;HBase是Apache Hadoop生態系統中的重要一員,主要用於海量結構化資料儲存;從邏輯上講,HBase將資料按

排序演算法大雜燴(概念複雜度程式碼實現應用例項

大部分內容來自於“一畫素”以及其他的網上資訊。待完善(關於不同版本程式碼的實現以及應用例項)目錄:一、常見演算法分類二、演算法複雜度三、相關概念四、演算法詳情以及程式碼實現一、 常見的排序演算法可分為兩類非線性時間比較類排序:通過比較來決定元素間的相對次序,由於其時間複雜度不

全屏沉浸式fitSystemWindow使用原理分析:全方位控制“沉浸式”的實現

前言 狀態列與導航欄屬於SystemUi的管理範疇,雖然介面的UI會受到SystemUi的影響,但是,APP並沒有直接繪製SystemUI的許可權與必要。APP端之所以能夠更改狀態列的顏色、導航欄的顏色,其實還是操作自己的View更改UI。可以這麼理解:狀態列

執行緒池 ThreadPool 概念應用例項

轉自: http://www.2cto.com/kf/201312/267018.html ThreadPool 先看成員變數Executor mExecutor。 執行緒池的基本思想還是一種物件池的思想,開闢一塊記憶體空間,裡面存放了眾多(未死亡)的執行緒,池中執行緒

SpringBoot Thymeleaf的介紹基礎語法頁面佈局

一、Thymeleaf介紹 簡單說,Thymeleaf 是一個跟 Velocity、FreeMarker 類似的模板引擎,它可以完

分散式服務框架Zookeeper介紹原理應用

轉載:https://www.jianshu.com/p/bf32e44d3113 分散式服務框架Zookeeper介紹、原理及應用 Zookeeper簡介 Zookeeper 分散式服務框架是 Apache Hadoop 的一個子專案,它主要是用來解決分散式應用中經常遇到的一些資料管理

05: 分布式文件系統 FastDFS原理 FastDFS配置應用

ble opp +++ load tin 分布式存儲 isa 4.2 libev 部署分布式存儲服務一、分布式文件系統介紹 二、配置分布式存儲服務2.1 配置主控節點(111) 裝包 修改配置文件 啟動服務 查看端口]# yum -y install