Linux Cgroup系列(01):Cgroup概述

cgroup和namespace相似,也是將進程進行分組,但它的目的和namespace不同,namespace是爲了隔離進程組之間的資源,而cgroup是爲了對一組進程進行統一的資源監控和限制。html

cgroup分v1v2兩個版本,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

爲何須要cgroup

在Linux裏,一直以來就有對進程進行分組的概念和需求,好比session group, progress group等,後來隨着人們對這方面的需求愈來愈多,好比須要追蹤一組進程的內存和IO使用狀況等,因而出現了cgroup,用來統一將進程進行分組,並在分組的基礎上對進程進行監控和資源控制管理等。ubuntu

什麼是cgroup

術語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就是一個這樣的例子。性能

如何查看當前系統支持哪些subsystem

能夠經過查看/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

從左到右,字段的含義分別是:

  1. subsystem的名字

  2. subsystem所關聯到的cgroup樹的ID,若是多個subsystem關聯到同一顆cgroup樹,那麼他們的這個字段將同樣,好比這裏的cpu和cpuacct就同樣,表示他們綁定到了同一顆樹。若是出現下面的狀況,這個字段將爲0:

    • 當前subsystem沒有和任何cgroup樹綁定

    • 當前subsystem已經和cgroup v2的樹綁定

    • 當前subsystem沒有被內核開啓

  3. subsystem所關聯的cgroup樹中進程組的個數,也即樹上節點的個數

  4. 1表示開啓,0表示沒有被開啓(能夠經過設置內核的啓動參數「cgroup_disable」來控制subsystem的開啓).

如何使用cgroup

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

如何查看當前進程屬於哪些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

每一行包含用冒號隔開的三列,他們的意思分別是

  1. cgroup樹的ID, 和/proc/cgroups文件中的ID一一對應。

  2. 和cgroup樹綁定的全部subsystem,多個subsystem之間用逗號隔開。這裏name=systemd表示沒有和任何subsystem綁定,只是給他起了個名字叫systemd。

  3. 進程在cgroup樹中的路徑,即進程所屬的cgroup,這個路徑是相對於掛載點的相對路徑。

全部的subsystems

目前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。

參考

相關文章
相關標籤/搜索