cgroup和namespace相似,也是將進程進行分組,但它的目的和namespace不同,namespace是爲了隔離進程組之間的資源,而cgroup是爲了對一組進程進行統一的資源監控和限制。html
cgroup分v1和v2兩個版本,v1實現較早,功能比較多,可是因爲它裏面的功能都是零零散散的實現的,因此規劃的不是很好,致使了一些使用和維護上的不便,v2的出現就是爲了解決v1中這方面的問題,在最新的4.5內核中,cgroup v2聲稱已經能夠用於生產環境了,但它所支持的功能還頗有限,隨着v2一塊兒引入內核的還有cgroup namespace。v1和v2能夠混合使用,可是這樣會更復雜,因此通常沒人會這樣用。node
本系列只介紹v1,由於這是目前你們正在用的版本,包括systemd,docker等。若是對v1比較熟悉的話,適應v2也不是問題。linux
本篇全部例子都在ubuntu-server-x86_64 16.04下執行經過docker
在Linux裏,一直以來就有對進程進行分組的概念和需求,好比session group, progress group等,後來隨着人們對這方面的需求愈來愈多,好比須要追蹤一組進程的內存和IO使用狀況等,因而出現了cgroup,用來統一將進程進行分組,並在分組的基礎上對進程進行監控和資源控制管理等。ubuntu
術語cgroup在不一樣的上下文中表明不一樣的意思,能夠指整個Linux的cgroup技術,也能夠指一個具體進程組。bash
cgroup是Linux下的一種將進程按組進行管理的機制,在用戶層看來,cgroup技術就是把系統中的全部進程組織成一顆一顆獨立的樹,每棵樹都包含系統的全部進程,樹的每一個節點是一個進程組,而每顆樹又和一個或者多個subsystem關聯,樹的做用是將進程分組,而subsystem的做用就是對這些組進行操做。cgroup主要包括下面兩部分:網絡
subsystem 一個subsystem就是一個內核模塊,他被關聯到一顆cgroup樹以後,就會在樹的每一個節點(進程組)上作具體的操做。subsystem常常被稱做"resource controller",由於它主要被用來調度或者限制每一個進程組的資源,可是這個說法不徹底準確,由於有時咱們將進程分組只是爲了作一些監控,觀察一下他們的狀態,好比perf_event subsystem。到目前爲止,Linux支持12種subsystem,好比限制CPU的使用時間,限制使用的內存,統計CPU的使用狀況,凍結和恢復一組進程等,後續會對它們一一進行介紹。session
hierarchy 一個hierarchy能夠理解爲一棵cgroup樹,樹的每一個節點就是一個進程組,每棵樹都會與零到多個subsystem關聯。在一顆樹裏面,會包含Linux系統中的全部進程,但每一個進程只能屬於一個節點(進程組)。系統中能夠有不少顆cgroup樹,每棵樹都和不一樣的subsystem關聯,一個進程能夠屬於多顆樹,即一個進程能夠屬於多個進程組,只是這些進程組和不一樣的subsystem關聯。目前Linux支持12種subsystem,若是不考慮不與任何subsystem關聯的狀況(systemd就屬於這種狀況),Linux裏面最多能夠建12顆cgroup樹,每棵樹關聯一個subsystem,固然也能夠只建一棵樹,而後讓這棵樹關聯全部的subsystem。當一顆cgroup樹不和任何subsystem關聯的時候,意味着這棵樹只是將進程進行分組,至於要在分組的基礎上作些什麼,將由應用程序本身決定,systemd就是一個這樣的例子。性能
能夠經過查看/proc/cgroups(since Linux 2.6.24)知道當前系統支持哪些subsystem,下面是一個例子測試
#subsys_name hierarchy num_cgroups enabled cpuset 11 1 1 cpu 3 64 1 cpuacct 3 64 1 blkio 8 64 1 memory 9 104 1 devices 5 64 1 freezer 10 4 1 net_cls 6 1 1 perf_event 7 1 1 net_prio 6 1 1 hugetlb 4 1 1 pids 2 68 1
從左到右,字段的含義分別是:
subsystem的名字
subsystem所關聯到的cgroup樹的ID,若是多個subsystem關聯到同一顆cgroup樹,那麼他們的這個字段將同樣,好比這裏的cpu和cpuacct就同樣,表示他們綁定到了同一顆樹。若是出現下面的狀況,這個字段將爲0:
當前subsystem沒有和任何cgroup樹綁定
當前subsystem已經和cgroup v2的樹綁定
當前subsystem沒有被內核開啓
subsystem所關聯的cgroup樹中進程組的個數,也即樹上節點的個數
1表示開啓,0表示沒有被開啓(能夠經過設置內核的啓動參數「cgroup_disable」來控制subsystem的開啓).
cgroup相關的全部操做都是基於內核中的cgroup virtual filesystem,使用cgroup很簡單,掛載這個文件系統就能夠了。通常狀況下都是掛載到/sys/fs/cgroup目錄下,固然掛載到其它任何目錄都不要緊。
這裏假設目錄/sys/fs/cgroup已經存在,下面用到的xxx爲任意字符串,取一個有意義的名字就能夠了,當用mount命令查看的時候,xxx會顯示在第一列
掛載一顆和全部subsystem關聯的cgroup樹到/sys/fs/cgroup
mount -t cgroup xxx /sys/fs/cgroup
掛載一顆和cpuset subsystem關聯的cgroup樹到/sys/fs/cgroup/cpuset
mkdir /sys/fs/cgroup/cpuset mount -t cgroup -o cpuset xxx /sys/fs/cgroup/cpuset
掛載一顆與cpu和cpuacct subsystem關聯的cgroup樹到/sys/fs/cgroup/cpu,cpuacct
mkdir /sys/fs/cgroup/cpu,cpuacct mount -t cgroup -o cpu,cpuacct xxx /sys/fs/cgroup/cpu,cpuacct
掛載一棵cgroup樹,但不關聯任何subsystem,下面就是systemd所用到的方式
mkdir /sys/fs/cgroup/systemd mount -t cgroup -o none,name=systemd xxx /sys/fs/cgroup/systemd
在不少使用systemd的系統中,好比ubuntu 16.04,systemd已經幫咱們將各個subsystem和cgroup樹關聯並掛載好了
dev@ubuntu:~$ mount|grep cgroup tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755) cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
建立並掛載好一顆cgroup樹以後,就有了樹的根節點,也即根cgroup,這時候就能夠經過建立文件夾的方式建立子cgroup,而後再往每一個子cgroup中添加進程。在後續介紹具體的subsystem的時候會詳細介紹如何操做cgroup。
注意
第一次掛載一顆和指定subsystem關聯的cgroup樹時,會建立一顆新的cgroup樹,當再一次用一樣的參數掛載時,會重用現有的cgroup樹,也即兩個掛載點看到的內容是同樣的。
#在ubuntu 16.04中,systemd已經將和cpu,cpuacct綁定的cgroup樹掛載到了/sys/fs/cgroup/cpu,cpuacct dev@ubuntu:~$ mount|grep /sys/fs/cgroup/cpu,cpuacct cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct,nsroot=/) #建立一個子目錄,用於後面的測試 dev@ubuntu:~$ sudo mkdir /sys/fs/cgroup/cpu,cpuacct/test dev@ubuntu:~$ ls -l /sys/fs/cgroup/cpu,cpuacct/|grep test drwxr-xr-x 2 root root 0 Oct 9 02:27 test #將和cpu,cpuacct關聯的cgroup樹從新mount到另一個目錄 dev@ubuntu:~$ mkdir -p ./cgroup/cpu,cpuacct && cd ./cgroup/ dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o cpu,cpuacct new-cpu-cpuacct ./cpu,cpuacct #在新目錄中看到的內容和/sys/fs/cgroup/cpu,cpuacct的同樣, #說明咱們將同一顆cgroup樹mount到了系統中的不一樣兩個目錄, #這顆cgroup樹和subsystem的關聯關係不變, #這點相似於mount同一塊硬盤到多個目錄 dev@ubuntu:~/cgroup$ ls -l ./cpu,cpuacct/ |grep test drwxr-xr-x 2 root root 0 Oct 9 02:27 test #清理 dev@ubuntu:~/cgroup$ sudo umount new-cpu-cpuacct
掛載一顆cgroup樹時,能夠指定多個subsystem與之關聯,但一個subsystem只能關聯到一顆cgroup樹,一旦關聯並在這顆樹上建立了子cgroup,subsystems和這棵cgroup樹就成了一個總體,不能再從新組合。以上面ubuntu 16.04爲例,因爲已經將cpu,cpuacct和一顆cgroup樹關聯而且他們下面有子cgroup了,因此就不能單獨的將cpu和另外一顆cgroup樹關聯。
#嘗試將cpu subsystem從新關聯一顆cgroup樹而且將這棵樹mount到./cpu目錄 dev@ubuntu:~/cgroup$ mkdir cpu dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o cpu new-cpu ./cpu mount: new-cpu is already mounted or /home/dev/cgroup/cpu busy #因爲cpu和cpuacct已經和一顆cgroup樹關聯了,因此這裏mount失敗 #嘗試將devices和pids關聯到同一顆樹上,因爲他們各自已經關聯到了不一樣的cgroup樹,因此mount失敗 dev@ubuntu:~/cgroup$ mkdir devices,pids dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o devices,pids new-devices-pids ./devices,pids mount: new-devices-pids is already mounted or /home/dev/cgroup/devices,pids busy
但因爲/sys/fs/cgroup/hugetlb和/sys/fs/cgroup/perf_event下沒有子cgroup,咱們能夠將他們從新組合。通常狀況下不會用到這個功能,一但最開始關聯好了以後,就不會去從新修改它,也即咱們通常不會去修改systemd給咱們設置好的subsystem和cgroup樹的關聯關係。
#/sys/fs/cgroup/hugetlb和/sys/fs/cgroup/perf_event裏面沒有子目錄,說明沒有子cgroup dev@ubuntu:~$ ls -l /sys/fs/cgroup/hugetlb|grep ^d dev@ubuntu:~$ ls -l /sys/fs/cgroup/perf_event|grep ^d #直接mount不行,由於perf_event,hugetlb已經被系統單獨mount過了 dev@ubuntu:~$ sudo mount -t cgroup -operf_event,hugetlb xxx /mnt mount: xxx is already mounted or /mnt busy #先umount dev@ubuntu:~$ sudo umount /sys/fs/cgroup/perf_event dev@ubuntu:~$ sudo umount /sys/fs/cgroup/hugetlb #若是系統默認安裝了lxcfs的話,lxcfs會將它們掛載在本身的目錄, #因此須要umount lxcfs及下面這兩個目錄,不然就沒有真正的umount掉perf_event和hugetlb dev@ubuntu:~$ sudo umount lxcfs dev@ubuntu:~$ sudo umount /run/lxcfs/controllers/hugetlb dev@ubuntu:~$ sudo umount /run/lxcfs/controllers/perf_event #再mount,成功 dev@ubuntu:~$ sudo mount -t cgroup -operf_event,hugetlb xxx /mnt dev@ubuntu:~$ ls /mnt/ cgroup.clone_children cgroup.sane_behavior hugetlb.2MB.limit_in_bytes hugetlb.2MB.usage_in_bytes release_agent cgroup.procs hugetlb.2MB.failcnt hugetlb.2MB.max_usage_in_bytes notify_on_release tasks #清理 dev@ubuntu:~$ sudo reboot
能夠建立任意多個不和任何subsystem關聯的cgroup樹,name是這棵樹的惟一標記,當name指定的是一個新的名字時,將建立一顆新的cgroup樹,但若是內核中已經存在一顆同樣name的cgroup樹,那麼將mount已存在的這顆cgroup樹
#因爲name=test的cgroup樹在系統中不存在,因此這裏會建立一顆新的name=test的cgroup樹 dev@ubuntu:~$ mkdir -p cgroup/test && cd cgroup dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o none,name=test test ./test #系統爲新建立的cgroup樹的root cgroup生成了默認文件 dev@ubuntu:~/cgroup$ ls ./test/ cgroup.clone_children cgroup.procs cgroup.sane_behavior notify_on_release release_agent tasks #新建立的cgroup樹的root cgroup裏包含系統中的全部進程 dev@ubuntu:~/cgroup$ wc -l ./test/cgroup.procs 131 ./test/cgroup.procs #建立子cgroup dev@ubuntu:~/cgroup$ cd test && sudo mkdir aaaa #系統已經爲新的子cgroup生成了默認文件 dev@ubuntu:~/cgroup/test$ ls aaaa cgroup.clone_children cgroup.procs notify_on_release tasks #新建立的子cgroup中沒有任何進程 dev@ubuntu:~/cgroup/test$ wc -l aaaa/cgroup.procs 0 aaaa/cgroup.procs #從新掛載這棵樹到test1,因爲mount的時候指定的name=test,因此和上面掛載的是同一顆cgroup樹,因而test1目錄下的內容和test目錄下的內容同樣 dev@ubuntu:~/cgroup/test$ cd .. && mkdir test1 dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o none,name=test test ./test1 dev@ubuntu:~/cgroup$ ls ./test1 aaaa cgroup.clone_children cgroup.procs cgroup.sane_behavior notify_on_release release_agent tasks #清理 dev@ubuntu:~/cgroup$ sudo umount ./test1 dev@ubuntu:~/cgroup$ sudo umount ./test dev@ubuntu:~/cgroup$ cd .. && rm -r ./cgroup
能夠經過查看/proc/[pid]/cgroup(since Linux 2.6.24)知道指定進程屬於哪些cgroup。
dev@ubuntu:~$ cat /proc/777/cgroup 11:cpuset:/ 10:freezer:/ 9:memory:/system.slice/cron.service 8:blkio:/system.slice/cron.service 7:perf_event:/ 6:net_cls,net_prio:/ 5:devices:/system.slice/cron.service 4:hugetlb:/ 3:cpu,cpuacct:/system.slice/cron.service 2:pids:/system.slice/cron.service 1:name=systemd:/system.slice/cron.service
每一行包含用冒號隔開的三列,他們的意思分別是
cgroup樹的ID, 和/proc/cgroups文件中的ID一一對應。
和cgroup樹綁定的全部subsystem,多個subsystem之間用逗號隔開。這裏name=systemd表示沒有和任何subsystem綁定,只是給他起了個名字叫systemd。
進程在cgroup樹中的路徑,即進程所屬的cgroup,這個路徑是相對於掛載點的相對路徑。
目前Linux支持下面12種subsystem
cpu (since Linux 2.6.24; CONFIG_CGROUP_SCHED)
用來限制cgroup的CPU使用率。
cpuacct (since Linux 2.6.24; CONFIG_CGROUP_CPUACCT)
統計cgroup的CPU的使用率。
cpuset (since Linux 2.6.24; CONFIG_CPUSETS)
綁定cgroup到指定CPUs和NUMA節點。
memory (since Linux 2.6.25; CONFIG_MEMCG)
統計和限制cgroup的內存的使用率,包括process memory, kernel memory, 和swap。
devices (since Linux 2.6.26; CONFIG_CGROUP_DEVICE)
限制cgroup建立(mknod)和訪問設備的權限。
freezer (since Linux 2.6.28; CONFIG_CGROUP_FREEZER)
suspend和restore一個cgroup中的全部進程。
net_cls (since Linux 2.6.29; CONFIG_CGROUP_NET_CLASSID)
將一個cgroup中進程建立的全部網絡包加上一個classid標記,用於tc和iptables。 只對發出去的網絡包生效,對收到的網絡包不起做用。
blkio (since Linux 2.6.33; CONFIG_BLK_CGROUP)
限制cgroup訪問塊設備的IO速度。
perf_event (since Linux 2.6.39; CONFIG_CGROUP_PERF)
對cgroup進行性能監控
net_prio (since Linux 3.3; CONFIG_CGROUP_NET_PRIO)
針對每一個網絡接口設置cgroup的訪問優先級。
hugetlb (since Linux 3.5; CONFIG_CGROUP_HUGETLB)
限制cgroup的huge pages的使用量。
pids (since Linux 4.3; CONFIG_CGROUP_PIDS)
限制一個cgroup及其子孫cgroup中的總進程數。
上面這些subsystem,有些須要作資源統計,有些須要作資源控制,有些即不統計也不控制。對於cgroup樹來講,有些subsystem嚴重依賴繼承關係,有些subsystem徹底用不到繼承關係,而有些對繼承關係沒有嚴格要求。
不一樣subsystem的工做方式可能差異較大,對系統性能的影響也不同,本人不是這方面的專家,後續文章中只會從功能的角度來介紹不一樣的subsystem,不會涉及到他們內部的實現。
本文介紹了cgroup的一些概念,包括subsystem和hierarchy,而後介紹了怎麼掛載cgroup文件系統以及12個subsystem的功能。從下一篇開始,將介紹cgroup具體的用法和不一樣的subsystem。