CGroup 是 Control Groups 的縮寫,是 Linux 內核提供的一種能夠限制、記錄、隔離進程組 (process groups) 所使用的物力資源 (如 cpu memory i/o 等等) 的機制。2007 年進入 Linux 2.6.24 內核,CGroups 不是全新創造的,它將進程管理從 cpuset 中剝離出來,做者是 Google 的 Paul Menage。CGroups 也是 LXC 爲實現虛擬化所使用的資源管理手段。css
CGroup 功能及組成java
CGroup 是將任意進程進行分組化管理的 Linux 內核功能。CGroup 自己是提供將進程進行分組化管理的功能和接口的基礎結構,I/O 或內存的分配控制等具體的資源管理功能是經過這個功能來實現的。這些具體的資源管理功能稱爲 CGroup 子系統或控制器。CGroup 子系統有控制內存的 Memory 控制器、控制進程調度的 CPU 控制器等。運行中的內核可使用的 Cgroup 子系統由/proc/cgroup 來確認。node
CGroup 提供了一個 CGroup 虛擬文件系統,做爲進行分組管理和各子系統設置的用戶接口。要使用 CGroup,必須掛載 CGroup 文件系統。這時經過掛載選項指定使用哪一個子系統。數組
CGroup 支持的文件種類網絡
表 1. CGroup 支持的文件種類數據結構
文件名 | R/W | 用途 |
---|---|---|
Release_agent多線程 |
RW架構 |
刪除分組時執行的命令,這個文件只存在於根分組dom |
Notify_on_releaseide |
RW |
設置是否執行 release_agent。爲 1 時執行 |
Tasks |
RW |
屬於分組的線程 TID 列表 |
Cgroup.procs |
R |
屬於分組的進程 PID 列表。僅包括多線程進程的線程 leader 的 TID,這點與 tasks 不一樣 |
Cgroup.event_control |
RW |
監視狀態變化和分組刪除事件的配置文件 |
CGroup 相關概念解釋
任務(task)。在 cgroups 中,任務就是系統的一個進程;
控制族羣(control group)。控制族羣就是一組按照某種標準劃分的進程。Cgroups 中的資源控制都是以控制族羣爲單位實現。一個進程能夠加入到某個控制族羣,也從一個進程組遷移到另外一個控制族羣。一個進程組的進程可使用 cgroups 以控制族羣爲單位分配的資源,同時受到 cgroups 以控制族羣爲單位設定的限制;
層級(hierarchy)。控制族羣能夠組織成 hierarchical 的形式,既一顆控制族羣樹。控制族羣樹上的子節點控制族羣是父節點控制族羣的孩子,繼承父控制族羣的特定的屬性;
子系統(subsystem)。一個子系統就是一個資源控制器,好比 cpu 子系統就是控制 cpu 時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起做用,一個子系統附加到某個層級之後,這個層級上的全部控制族羣都受到這個子系統的控制。
相互關係
每次在系統中建立新層級時,該系統中的全部任務都是那個層級的默認 cgroup(咱們稱之爲 root cgroup,此 cgroup 在建立層級時自動建立,後面在該層級中建立的 cgroup 都是此 cgroup 的後代)的初始成員;
一個子系統最多隻能附加到一個層級;
一個層級能夠附加多個子系統;
一個任務能夠是多個 cgroup 的成員,可是這些 cgroup 必須在不一樣的層級;
系統中的進程(任務)建立子進程(任務)時,該子任務自動成爲其父進程所在 cgroup 的成員。而後可根據須要將該子任務移動到不一樣的 cgroup 中,但開始時它老是繼承其父任務的 cgroup。
圖 1. CGroup 層級圖
圖 1 所示的 CGroup 層級關係顯示,CPU 和 Memory 兩個子系統有本身獨立的層級系統,而又經過 Task Group 取得關聯關係。
CGroup 特色
在 cgroups 中,任務就是系統的一個進程。
控制族羣(control group)。控制族羣就是一組按照某種標準劃分的進程。Cgroups 中的資源控制都是以控制族羣爲單位實現。一個進程能夠加入到某個控制族羣,也從一個進程組遷移到另外一個控制族羣。一個進程組的進程可使用 cgroups 以控制族羣爲單位分配的資源,同時受到 cgroups 以控制族羣爲單位設定的限制。
層級(hierarchy)。控制族羣能夠組織成 hierarchical 的形式,既一顆控制族羣樹。控制族羣樹上的子節點控制族羣是父節點控制族羣的孩子,繼承父控制族羣的特定的屬性。
子系統(subsytem)。一個子系統就是一個資源控制器,好比 cpu 子系統就是控制 cpu 時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起做用,一個子系統附加到某個層級之後,這個層級上的全部控制族羣都受到這個子系統的控制。
CGroup 應用架構
圖 2. CGroup 典型應用架構圖
如圖 2 所示,CGroup 技術能夠被用來在操做系統底層限制物理資源,起到 Container 的做用。圖中每個 JVM 進程對應一個 Container Cgroup 層級,經過 CGroup 提供的各種子系統,能夠對每個 JVM 進程對應的線程級別進行物理限制,這些限制包括 CPU、內存等等許多種類的資源。下一部分會具體對應用程序進行 CPU 資源隔離進行演示。
講解 CGroup 設計原理前,咱們先來作一個簡單的實驗。實驗基於 Linux Centosv6.564 位版本,JDK1.7。實驗目的是運行一個佔用 CPU 的 Java 程序,若是不用 CGroup 物理隔離 CPU 核,那程序會由操做系統層級自動挑選 CPU 核來運行程序。因爲操做系統層面採用的是時間片輪詢方式隨機挑選 CPU 核做爲運行容器,因此會在本機器上 24 個 CPU 核上隨機執行。若是採用 CGroup 進行物理隔離,咱們能夠選擇某些 CPU 核做爲指定運行載體。
清單 1.Java 程序代碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
清單 1 程序會啓動 10 個線程,這 10 個線程都在作佔用 CPU 的計算工做,它們可能會運行在 1 個 CPU 核上,也可能運行在多個核上,由操做系統決定。咱們稍後會在 Linux 機器上經過命令在後臺運行清單 1 程序。本實驗須要對 CPU 資源進行限制,因此咱們在 cpu_and_set 子系統上建立本身的層級「zhoumingyao」。
清單 2. 建立層級
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
經過 mkdir 命令新建文件夾 zhoumingyao,因爲已經預先加載 cpu_and_set 子系統成功,因此當文件夾建立完畢的同時,cpu_and_set 子系統對應的文件夾也會自動建立。
運行 Java 程序前,咱們須要確認 cpu_and_set 子系統安裝的目錄,如清單 3 所示。
清單 3. 確認目錄
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
輸出顯示 cpuset_cpu 的目錄是 cpuset,cpu:/zhoumingyao,因爲本實驗所採用的 Java 程序是多線程程序,因此須要使用 cgexec 命令來幫助啓動,而不能如網絡上有些材料所述,採用 java –jar 命令啓動後,將 pid 進程號填入 tasks 文件便可的錯誤方式。清單 4 即採用 cgexec 命令啓動 java 程序,須要使用到清單 3 定位到的 cpuset_cpu 目錄地址。
清單 4. 運行 Java 程序
[root@facenode4 zhoumingyao]# cgexec -g cpuset,cpu:/zhoumingyao java -jar test.jars
咱們在 cpuset.cpus 文件中設置須要限制只有 0-10 這 11 個 CPU 核能夠被用來運行上述清單 4 啓動的 Java 多線程程序。固然 CGroup 還能夠限制具體每一個核的使用百分比,這裏再也不作過多的描述,請讀者自行翻閱 CGroup 官方材料。
清單 5.cpu 核限制
1 2 |
|
接下來,經過 TOP 命令得到清單 4 啓動的 Java 程序的全部相關線程 ID,將這些 ID 寫入到 Tasks 文件。
清單 6. 設置線程 ID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
所有設置完畢後,咱們能夠經過 TOP 命令查看具體的每一顆 CPU 核上的運行狀況,發現只有 0-10 這 11 顆 CPU 核上有計算資源被調用,能夠進一步經過 TOP 命令確認所有都是清單 4 所啓動的 Java 多線程程序的線程。
清單 7. 運行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
整體上來講,CGroup 的使用方式較爲簡單,目前主要的問題是網絡上已有的中文材料缺乏詳細的配置步驟,一旦讀者經過反覆實驗,掌握了配置方式,使用上應該不會有大的問題。
CGroups 的源代碼較爲清晰,咱們能夠從進程的角度出發來剖析 cgroups 相關數據結構之間的關係。在 Linux 中,管理進程的數據結構是 task_struct,其中與 cgroups 有關的代碼如清單 8 所示:
清單 8.task_struct 代碼
1 2 3 4 5 6 |
|
其中 cgroups 指針指向了一個 css_set 結構,而 css_set 存儲了與進程有關的 cgroups 信息。cg_list 是一個嵌入的 list_head 結構,用於將連到同一個 css_set 的進程組織成一個鏈表。下面咱們來看 css_set 的結構,代碼如清單 9 所示:
清單 9.css_set 代碼
1 2 3 4 5 6 7 8 |
|
其中 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 代碼
1 2 3 4 5 6 |
|
cgroup 指針指向了一個 cgroup 結構,也就是進程屬於的 cgroup。進程受到子系統的控制,其實是經過加入到特定的 cgroup 實現的,由於 cgroup 在特定的層級上,而子系統又是附和到上面的。經過以上三個結構,進程就能夠和 cgroup 鏈接起來了:task_struct->css_set->cgroup_subsys_state->cgroup。cgroup 結構如清單 11 所示:
清單 11.cgroup 代碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
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 代碼
1 2 3 4 5 |
|
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 代碼
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
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 代碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
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 技術、虛擬化技術的發展奠基了技術基礎,本文的目標是讓初學者能夠經過本身動手的方式簡單地理解技術,將起步門檻放低。