cgroup是Linux內(nèi)核提供的一種可以對進程或進程組進行物理資源(如:CPU,內(nèi)存,設(shè)備IO等)限制、隔離和統(tǒng)計的機制。本文對cgroup進行簡單介紹,并從cgroup的層級關(guān)系入手分析進程和cgroup多對多的實現(xiàn)方式。
Part 01●??cgroup概述?●
cgroup是Control Groups的縮寫,是Linux內(nèi)核提供的一種可以對進程或進程組進行物理資源(如:CPU,內(nèi)存,設(shè)備IO等)限制、隔離和統(tǒng)計的機制。cgroup的用戶空間管理是通過cgroup文件系統(tǒng)來實現(xiàn),得益于Linux的虛擬文件系統(tǒng),其文件系統(tǒng)的細節(jié)被隱藏,用戶通過相關(guān)的控制文件來實現(xiàn)該功能的使用。
cgroup于2.6內(nèi)核時期由Google公司主導(dǎo)引入,它是Linux內(nèi)核實現(xiàn)資源虛擬化的技術(shù)基礎(chǔ),是LXC(Linux Containers)和Docker容器的技術(shù)基石。cgroup中有如下相關(guān)概念:
任務(wù)(task):進程的別稱;
控制組(control group):按照某種標(biāo)準(zhǔn)劃分的進程集合。Cgroup中的資源控制都是以控制組為單位來實現(xiàn)。進程可以加入到某個控制組,也可以從一個進程組遷移到另一個控制組中。一個進程組的進程可以使用cgroups以控制組為單位分配的資源,同時受到cgroup以控制組為單位設(shè)置的資源限制。
層級(hierarchy):控制組的層級關(guān)系,采用樹的結(jié)構(gòu)方式組織,子節(jié)點的控制組繼承父節(jié)點的資源設(shè)置屬性。
子系統(tǒng)(subsystem):一個子系統(tǒng)就是一種資源控制器,比如cpu子系統(tǒng)可以控制進程CPU使用時間分配,如圖1所示。子系統(tǒng)必須附件到一個層級上才能起作用,一個子系統(tǒng)附加到某個層級以后,這個層級上的所有控制組都受到這個子系統(tǒng)的控制。
Part 02●??cgroup子系統(tǒng)?●
cgroup子系統(tǒng)和內(nèi)核版本有關(guān),隨著內(nèi)核的迭代,能限制的資源也越來越多,一般包括如下子系統(tǒng)。
? blkio:對輸入/輸出訪問存取塊設(shè)備設(shè)定限制,比如物理設(shè)備(磁盤,固態(tài)硬盤,USB等等)。
? cpu:限制進程的cpu使用,涉及cpu調(diào)度時間片分配。
? cpuacct:自動生成cgroup中任務(wù)所使用的cpu報告。
? cpuset:為cgroup中的任務(wù)分配獨立cpu(多核系統(tǒng))和內(nèi)存節(jié)點。
? devices:允許或者拒絕cgroup中的任務(wù)訪問設(shè)備.
? freezer:掛起或恢復(fù)cgroup中的任務(wù)。
? memory:設(shè)定cgroup中任務(wù)使用的內(nèi)存限制,并自動生成由那些任務(wù)使用的內(nèi)存資源報告。
? net_cls:使用等級識別符標(biāo)記網(wǎng)絡(luò)數(shù)據(jù)包,可允許Linux流浪控制程序識別從具體cgroup中生成的數(shù)據(jù)包。
? ns:namespace子系統(tǒng)。
Part 03●??cgroup層級規(guī)則?●
結(jié)合cgroup層級(hierarchy)可以理解為一顆樹,樹的每個節(jié)點就是一個進程組,每棵樹都會與一到多個子系統(tǒng)關(guān)聯(lián)。在一棵樹里,會包含Linux系統(tǒng)中的所有進程,但每個進程只能屬于一個節(jié)點(進程組)。系統(tǒng)中可以有很多顆cgroup樹,每棵樹都和不同的subsystem關(guān)聯(lián),一個進程可以屬于多棵樹,即一個進程可以屬于多個進程組,只是這些進程組和不同的子系統(tǒng)關(guān)聯(lián)。目前Linux最多可以建十二顆cgroup樹,每棵樹關(guān)聯(lián)一個子系統(tǒng),當(dāng)然也可以只建一棵樹,然后讓這棵樹關(guān)聯(lián)到所有的子系統(tǒng)。當(dāng)一顆cgroup樹不和任何子系統(tǒng)關(guān)聯(lián)的時候,意味著這棵樹只是將進程進行分組,至于要在分組的基礎(chǔ)上做些什么,將由應(yīng)用程序自己決定,systemd就是這樣一個例子。
層級的組成規(guī)則有四個,描述如下:
規(guī)則1:單個層次結(jié)構(gòu)可以具有一個或多個子系統(tǒng)。如圖1所示,/cpu_memory_cg這個層級對cgroup1,cgroup2設(shè)置了cpu和memory兩個子系統(tǒng)。
圖1 層級規(guī)則1
規(guī)則2:如果任何一個子系統(tǒng)已經(jīng)附加到了一個層次,則不能將他們附加到另一個層次的結(jié)構(gòu)中。如圖2所示,層級A的cpu_cg首先管理cpu子系統(tǒng),那么層級B的cpu_mem_cg就無法管理cpu子系統(tǒng)。
圖2 cgroup層級規(guī)則2
規(guī)則3:每次在系統(tǒng)上創(chuàng)建新的層次結(jié)構(gòu)時,系統(tǒng)上的所有任務(wù)最初都是該層次結(jié)構(gòu)的默認cgroup(稱為根cgroup)成員。對于創(chuàng)建的任何單個層次結(jié)構(gòu),系統(tǒng)上的每個任務(wù)都可以是該層次結(jié)構(gòu)中的一個cgroup成員。一個任務(wù)可以位于多個cgroup中,只要這些cgroup中的每個處于不同的子系統(tǒng)層次結(jié)構(gòu)中即可。任務(wù)一旦成為同一層次結(jié)構(gòu)中的第二個cgroup成員,就會將其從該層次結(jié)構(gòu)中的第一個cgroup中刪除,即在同一層次結(jié)構(gòu)中的兩個不通cgroup,絕不會有同一任務(wù),也即是對某進程某類cgroup子系統(tǒng)的限制方式只能有一種。創(chuàng)建第一個層次結(jié)構(gòu)時,系統(tǒng)上的每個任務(wù)都是至少一個cgroup(根cgroup)的成員,因此,在使用cgroup時,每個系統(tǒng)任務(wù)始終至少位于一個cgroup中,如圖3所示。
圖3 cgroup層級規(guī)則3
規(guī)則4:系統(tǒng)上派生的任何進程都會創(chuàng)建一個子進程(或線程)。子進程自動繼承其父級的cgroup成員資格,但可以根據(jù)需要移動到其他cgroup中,移動后父子進程完全獨立,如圖4所示。
圖4 cgroup層級規(guī)則4
Part 04●??cgroup層級關(guān)系分析?●
我們從進程的角度出發(fā),結(jié)合源碼中的數(shù)據(jù)結(jié)構(gòu)來解析cgroups相關(guān)數(shù)據(jù)之間的關(guān)系。首先在Linux中,管理進程的數(shù)據(jù)結(jié)構(gòu)是task_struct,其中與cgroups有關(guān)的成員如下:
其中cgroup指向一個css_set結(jié)構(gòu),其存儲了與進程相關(guān)的cgroups信息。cg_list為使用同一個css_set的進程鏈表。css_set結(jié)構(gòu)如下:
結(jié)構(gòu)體的元素信息解釋如下:
refcount是css_set的引用計數(shù),其可以被多個進程共用,只要這些進程的cgroups信息相同。比如,在所有已經(jīng)創(chuàng)建的層級里面都在同一個cgroup里的進程。
hlist用于把所有css_set構(gòu)建成一個hash表,內(nèi)核能快速查找特定的css_set。
tasks將所有引用此css_set的進程鏈接成鏈表。
cg_links指向一個由struct cg_group_link組成的鏈表
subsys為一個指針數(shù)組,存儲一組指向cgroup_subsys_state的指針。一個cgroup_subsys_state就是進程與一個特定的子系統(tǒng)相關(guān)的信息。通過這個指針,進程就可以獲得相應(yīng)的cgroups控制信息。
接下來我們看一下cgroup_subsys_state結(jié)構(gòu)體情況:
結(jié)構(gòu)體中cgroup指針指向一個cgroup結(jié)構(gòu),進程受到子系統(tǒng)的資源控制,實際上是通過加入特定的cgroup子系統(tǒng)實現(xiàn),因為cgroup在特定的層級上,而子系統(tǒng)又是附加到層級上的。
我們來看看cgroup的結(jié)構(gòu),
sibling,children和parent三個鏈表負責(zé)將同一層級的cgroup連接成一棵樹。
susys為之前描述過的子系統(tǒng)指針數(shù)組。
root指向了一個cgroupfs_root的結(jié)構(gòu),就是cgroup所在的層級對應(yīng)的結(jié)構(gòu)體。
root->top_cgroup指向所在層級的根cgroup,也就是幻劍層級時自動創(chuàng)建的那個cgroup。獲取層級的根cgroup可以通過cgroup->root->top_cgroup。
css_sets指向一個由cg_cgroup_link的鏈表,和css_set中cg_links一致。
為了理清楚css_set和cgroup的關(guān)系,我們還需對中間層的cg_cgroup_link結(jié)構(gòu)進行分析,結(jié)構(gòu)體數(shù)據(jù)如下:
結(jié)構(gòu)體中的數(shù)據(jù)說明如下:
cgrp_link_list鏈接到cgroup->css_sets指向的鏈表。
cgrp則指向此cg_cgroup_link相關(guān)的group。
cg_link_list則鏈接到css_set->cg_links指向的鏈表。
cg則指向cg_cgroup_link相關(guān)的css_set。
可以看出cgroup和css_set實際上是一個多對多的關(guān)系,需要添加一個中間結(jié)構(gòu)將兩者結(jié)合,cg_group_link中的cgrp和cg元素就是結(jié)合部,cgrp_link_list和cg_link_list兩個鏈表即為掛接的cgroup和css_set實體,方便輪詢。
從cgroup的層級規(guī)則中可以看出,一組進程可以同屬于不在同一層級的cgroup,相結(jié)合理解,一個css_set存儲了一組進程根各個子系統(tǒng)相關(guān)的信息,子系統(tǒng)來自不通的cgroup層級,因此一個css_set存儲的cgroup_subsys_state可以對應(yīng)多個cgroup。? ? 另一方面,cgroup層級也存儲了一組cgroup_subsys_state,其從cgroup所在的層級附加的子系統(tǒng)中獲得,一個cgroup可以有多個進程,進程的css_set不一定相同,因為進程可能使用了多個層級,所以一個cgroup也需要對應(yīng)多個css_set。圖5詳細描述了多對多的掛接關(guān)系。
圖5 進程和cgroup多對多關(guān)系圖
Part 05●??結(jié)語?●
本文在cgroup概念基礎(chǔ)上,對其和進程之間多對多的關(guān)系進行了拆解,從相關(guān)結(jié)構(gòu)體中變量的掛接分析其具體代碼實現(xiàn)方式,希望能幫助讀者對cgroup層級關(guān)系和使用方式有更好的理解。