Linux Cgroup系列(02):建立並管理cgroup

本文將建立並掛載一顆不和任何subsystem綁定的cgroup樹,用來演示怎麼建立、刪除子cgroup,以及如何往cgroup中添加和刪除進程。shell

因爲不和任何subsystem綁定,因此這棵樹沒有任何實際的功能,但這不影響咱們的演示,還有一個好處就是咱們不會受subsystem功能的影響,能夠將精力集中在cgroup樹上。ubuntu

本篇全部例子都在ubuntu-server-x86_64 16.04下執行經過bash

掛載cgroup樹

開始使用cgroup前須要先掛載cgroup樹,下面先看看如何掛載一顆cgroup樹,而後再查看其根目錄下生成的文件線程

#準備須要的目錄
dev@ubuntu:~$ mkdir cgroup && cd cgroup
dev@ubuntu:~/cgroup$ mkdir demo

#因爲name=demo的cgroup樹不存在,因此係統會建立一顆新的cgroup樹,而後掛載到demo目錄
dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o none,name=demo demo ./demo

#掛載點所在目錄就是這顆cgroup樹的root cgroup,在root cgroup下面,系統生成了一些默認文件
dev@ubuntu:~/cgroup$ ls ./demo/
cgroup.clone_children  cgroup.procs  cgroup.sane_behavior  notify_on_release  release_agent  tasks

#cgroup.procs裏包含系統中的全部進程
dev@ubuntu:~/cgroup$ wc -l ./demo/cgroup.procs
131 ./demo/cgroup.procs

下面是每一個文件的含義:日誌

  • cgroup.clone_children
    這個文件只對cpuset(subsystem)有影響,當該文件的內容爲1時,新建立的cgroup將會繼承父cgroup的配置,即從父cgroup裏面拷貝配置文件來初始化新cgroup,能夠參考這裏code

  • cgroup.procs
    當前cgroup中的全部進程ID,系統不保證ID是順序排列的,且ID有可能重複server

  • cgroup.sane_behavior
    具體功能不詳,能夠參考這裏這裏繼承

  • notify_on_release
    該文件的內容爲1時,當cgroup退出時(再也不包含任何進程和子cgroup),將調用release_agent裏面配置的命令。新cgroup被建立時將默認繼承父cgroup的這項配置。進程

  • release_agent
    裏面包含了cgroup退出時將會執行的命令,系統調用該命令時會將相應cgroup的相對路徑看成參數傳進去。 注意:這個文件只會存在於root cgroup下面,其餘cgroup裏面不會有這個文件。rem

  • tasks
    當前cgroup中的全部線程ID,系統不保證ID是順序排列的

後面在介紹如何往cgroup中添加進程時會介紹cgroup.procs和tasks的差異。

建立和刪除cgroup

掛載好上面的cgroup樹以後,就能夠在裏面建子cgroup了

#建立子cgroup很簡單,新建一個目錄就能夠了
dev@ubuntu:~/cgroup$ cd demo
dev@ubuntu:~/cgroup/demo$ sudo mkdir cgroup1

#在新建立的cgroup裏面,系統默認也生成了一些文件,這些文件的意義和root cgroup裏面的同樣
dev@ubuntu:~/cgroup/demo$ ls cgroup1/
cgroup.clone_children  cgroup.procs  notify_on_release  tasks

#新建立的cgroup裏沒有任何進程和線程
dev@ubuntu:~/cgroup/demo$ wc -l cgroup1/cgroup.procs
0 cgroup1/cgroup.procs
dev@ubuntu:~/cgroup/demo$ wc -l cgroup1/tasks
0 cgroup1/tasks

#每一個cgroup均可以建立本身的子cgroup,因此咱們也能夠在cgroup1裏面建立子cgroup
dev@ubuntu:~/cgroup/demo$ sudo mkdir cgroup1/cgroup11
dev@ubuntu:~/cgroup/demo$ ls cgroup1/cgroup11
cgroup.clone_children  cgroup.procs  notify_on_release  tasks

#刪除cgroup也很簡單,刪除掉相應的目錄就能夠了
dev@ubuntu:~/cgroup/demo$ sudo rmdir cgroup1/
rmdir: failed to remove 'cgroup1/': Device or resource busy
#這裏刪除cgroup1失敗,是由於它裏面包含了子cgroup,因此不能刪除,
#若是cgroup1包含有進程或者線程,也會刪除失敗

#先刪除cgroup11,再刪除cgroup1就能夠了
dev@ubuntu:~/cgroup/demo$ sudo rmdir cgroup1/cgroup11/
dev@ubuntu:~/cgroup/demo$ sudo rmdir cgroup1/

添加進程

建立新的cgroup後,就能夠往裏面添加進程了。注意下面幾點:

  • 在一顆cgroup樹裏面,一個進程必需要屬於一個cgroup。

  • 新建立的子進程將會自動加入父進程所在的cgroup。

  • 從一個cgroup移動一個進程到另外一個cgroup時,只要有目的cgroup的寫入權限就能夠了,系統不會檢查源cgroup裏的權限。

  • 用戶只能操做屬於本身的進程,不能操做其餘用戶的進程,root帳號除外。

#--------------------------第一個shell窗口----------------------
#建立一個新的cgroup
dev@ubuntu:~/cgroup/demo$ sudo mkdir test
dev@ubuntu:~/cgroup/demo$ cd test

#將當前bash加入到上面新建立的cgroup中
dev@ubuntu:~/cgroup/demo/test$ echo $$
1421
dev@ubuntu:~/cgroup/demo/test$ sudo sh -c 'echo 1421 > cgroup.procs'
#注意:一次只能往這個文件中寫一個進程ID,若是須要寫多個的話,須要屢次調用這個命令

#--------------------------第二個shell窗口----------------------
#從新打開一個shell窗口,避免第一個shell裏面運行的命令影響輸出結果
#這時能夠看到cgroup.procs裏面包含了上面的第一個shell進程
dev@ubuntu:~/cgroup/demo/test$ cat cgroup.procs
1421

#--------------------------第一個shell窗口----------------------
#回到第一個窗口,運行top命令
dev@ubuntu:~/cgroup/demo/test$ top
#這裏省略輸出內容

#--------------------------第二個shell窗口----------------------
#這時再在第二個窗口查看,發現top進程自動和它的父進程(1421)屬於同一個cgroup
dev@ubuntu:~/cgroup/demo/test$ cat cgroup.procs
1421
16515
dev@ubuntu:~/cgroup/demo/test$ ps -ef|grep top
dev      16515  1421  0 04:02 pts/0    00:00:00 top
dev@ubuntu:~/cgroup/demo/test$

#在一顆cgroup樹裏面,一個進程必需要屬於一個cgroup,
#因此咱們不能憑空從一個cgroup裏面刪除一個進程,只能將一個進程從一個cgroup移到另外一個cgroup,
#這裏咱們將1421移動到root cgroup
dev@ubuntu:~/cgroup/demo/test$ sudo sh -c 'echo 1421 > ../cgroup.procs'
dev@ubuntu:~/cgroup/demo/test$ cat cgroup.procs
16515
#移動1421到另外一個cgroup以後,它的子進程不會隨着移動

#--------------------------第一個shell窗口----------------------
##回到第一個shell窗口,進行清理工做
#先用ctrl+c退出top命令
dev@ubuntu:~/cgroup/demo/test$ cd ..
#而後刪除建立的cgroup
dev@ubuntu:~/cgroup/demo$ sudo rmdir test

權限

上面咱們都是用sudo(root帳號)來操做的,但實際上普通帳號也能夠操做cgroup

#建立一個新的cgroup,並修改他的owner
dev@ubuntu:~/cgroup/demo$ sudo mkdir permission
dev@ubuntu:~/cgroup/demo$ sudo chown -R dev:dev ./permission/

#1421原來屬於root cgroup,雖然dev沒有root cgroup的權限,但仍是能夠將1421移動到新的cgroup下,
#說明在移動進程的時候,系統不會檢查源cgroup裏的權限。
dev@ubuntu:~/cgroup/demo$ echo 1421 > ./permission/cgroup.procs

#因爲dev沒有root cgroup的權限,再把1421移回root cgroup失敗
dev@ubuntu:~/cgroup/demo$ echo 1421 > ./cgroup.procs
-bash: ./cgroup.procs: Permission denied

#找一個root帳號的進程
dev@ubuntu:~/cgroup/demo$ ps -ef|grep /lib/systemd/systemd-logind
root       839     1  0 01:52 ?        00:00:00 /lib/systemd/systemd-logind
#由於該進程屬於root,dev沒有操做它的權限,因此將該進程加入到permission中失敗
dev@ubuntu:~/cgroup/demo$ echo 839 >./permission/cgroup.procs
-bash: echo: write error: Permission denied
#只能由root帳號添加
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 839 >./permission/cgroup.procs'

#dev還能夠在permission下建立子cgroup
dev@ubuntu:~/cgroup/demo$ mkdir permission/c1
dev@ubuntu:~/cgroup/demo$ ls permission/c1
cgroup.clone_children  cgroup.procs  notify_on_release  tasks

#清理
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 839 >./cgroup.procs'
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 1421 >./cgroup.procs'
dev@ubuntu:~/cgroup/demo$ rmdir permission/c1
dev@ubuntu:~/cgroup/demo$ sudo rmdir permission

cgroup.procs vs tasks

上面提到cgroup.procs包含的是進程ID, 而tasks裏面包含的是線程ID,那麼他們有什麼區別呢?

#建立兩個新的cgroup用於演示
dev@ubuntu:~/cgroup/demo$ sudo mkdir c1 c2

#爲了便於操做,先給root帳號設置一個密碼,而後切換到root帳號
dev@ubuntu:~/cgroup/demo$ sudo passwd root
dev@ubuntu:~/cgroup/demo$ su root
root@ubuntu:/home/dev/cgroup/demo#

#系統中找一個有多個線程的進程
root@ubuntu:/home/dev/cgroup/demo# ps -efL|grep /lib/systemd/systemd-timesyncd
systemd+   610     1   610  0    2 01:52 ?        00:00:00 /lib/systemd/systemd-timesyncd
systemd+   610     1   616  0    2 01:52 ?        00:00:00 /lib/systemd/systemd-timesyncd
#進程610有兩個線程,分別是610和616

#將616加入c1/cgroup.procs
root@ubuntu:/home/dev/cgroup/demo# echo 616 > c1/cgroup.procs
#因爲cgroup.procs存放的是進程ID,因此這裏看到的是616所屬的進程ID(610)
root@ubuntu:/home/dev/cgroup/demo# cat c1/cgroup.procs
610
#從tasks中的內容能夠看出,雖然只往cgroup.procs中加了線程616,
#但系統已經將這個線程所屬的進程的全部線程都加入到了tasks中,
#說明如今整個進程的全部線程已經處於c1中了
root@ubuntu:/home/dev/cgroup/demo# cat c1/tasks
610
616

#將616加入c2/tasks中
root@ubuntu:/home/dev/cgroup/demo# echo 616 > c2/tasks

#這時咱們看到雖然在c1/cgroup.procs和c2/cgroup.procs裏面都有610,
#但c1/tasks和c2/tasks中包含了不一樣的線程,說明這個進程的兩個線程分別屬於不一樣的cgroup
root@ubuntu:/home/dev/cgroup/demo# cat c1/cgroup.procs
610
root@ubuntu:/home/dev/cgroup/demo# cat c1/tasks
610
root@ubuntu:/home/dev/cgroup/demo# cat c2/cgroup.procs
610
root@ubuntu:/home/dev/cgroup/demo# cat c2/tasks
616
#經過tasks,咱們能夠實現線程級別的管理,但一般狀況下不會這麼用,
#而且在cgroup V2之後,將再也不支持該功能,只能以進程爲單位來配置cgroup

#清理
root@ubuntu:/home/dev/cgroup/demo# echo 610 > ./cgroup.procs
root@ubuntu:/home/dev/cgroup/demo# rmdir c1
root@ubuntu:/home/dev/cgroup/demo# rmdir c2
root@ubuntu:/home/dev/cgroup/demo# exit
exit

release_agent

當一個cgroup裏沒有進程也沒有子cgroup時,release_agent將被調用來執行cgroup的清理工做。

#建立新的cgroup用於演示
dev@ubuntu:~/cgroup/demo$ sudo mkdir test
#先enable release_agent
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 1 > ./test/notify_on_release'

#而後建立一個腳本/home/dev/cgroup/release_demo.sh,
#通常狀況下都會利用這個腳本執行一些cgroup的清理工做,但咱們這裏爲了演示簡單,僅僅只寫了一條日誌到指定文件
dev@ubuntu:~/cgroup/demo$ cat > /home/dev/cgroup/release_demo.sh << EOF
#!/bin/bash
echo \$0:\$1 >> /home/dev/release_demo.log
EOF

#添加可執行權限
dev@ubuntu:~/cgroup/demo$ chmod +x ../release_demo.sh

#將該腳本設置進文件release_agent
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo /home/dev/cgroup/release_demo.sh > ./release_agent'
dev@ubuntu:~/cgroup/demo$ cat release_agent
/home/dev/cgroup/release_demo.sh

#往test裏面添加一個進程,而後再移除,這樣就會觸發release_demo.sh
dev@ubuntu:~/cgroup/demo$ echo $$
27597
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 27597 > ./test/cgroup.procs'
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 27597 > ./cgroup.procs'

#從日誌能夠看出,release_agent被觸發了,/test是cgroup的相對路徑
dev@ubuntu:~/cgroup/demo$ cat /home/dev/release_demo.log
/home/dev/cgroup/release_demo.sh:/test

結束語

本文介紹瞭如何操做cgroup,因爲沒有和任何subsystem關聯,因此在這顆樹上的全部操做都沒有實際的功能,不會對系統有影響。從下一篇開始,將介紹具體的subsystem。

參考

相關文章
相關標籤/搜索