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