Linux Cgroup系列(03):限制cgroup的進程數(subsystem之pids)

上一篇文章中介紹瞭如何管理cgroup,從這篇開始將介紹具體的subsystem。node

本篇將介紹一個簡單的subsystem,名字叫pids,功能是限制cgroup及其全部子孫cgroup裏面能建立的總的task數量。shell

注意:這裏的task指經過fork和clone函數建立的進程,因爲clone函數也能建立線程(在Linux裏面,線程是一種特殊的進程),因此這裏的task也包含線程,本文統一以進程來表明task,即本文中的進程表明了進程和線程ubuntu

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

建立子cgroup

在ubuntu 16.04裏面,systemd已經幫咱們將各個subsystem和cgroup樹綁定並掛載好了,咱們直接用現成的就能夠了。bash

#從這裏的輸出能夠看到,pids已經被掛載在了/sys/fs/cgroup/pids,這是systemd作的
dev@dev:~$ mount|grep pids
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)

建立子cgroup,取名爲test函數

#進入目錄/sys/fs/cgroup/pids/並新建一個目錄,即建立了一個子cgroup
dev@dev:~$ cd /sys/fs/cgroup/pids/
dev@dev:/sys/fs/cgroup/pids$ sudo mkdir test
#這裏將test目錄的owner設置成dev帳號,這樣後續操做就不用每次都敲sudo了,省去麻煩
dev@dev:/sys/fs/cgroup/pids$ sudo chown -R dev:dev ./test/

再來看看test目錄下的文件ui

#除了上一篇中介紹的那些文件外,多了兩個文件
dev@dev:/sys/fs/cgroup/pids$ cd test
dev@dev:/sys/fs/cgroup/pids/test$ ls
cgroup.clone_children  cgroup.procs  notify_on_release  pids.current  pids.max  tasks

下面是這兩個文件的含義:線程

  • pids.current: 表示當前cgroup及其全部子孫cgroup中現有的總的進程數量code

    #因爲這是個新建立的cgroup,因此裏面尚未任何進程
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current 
    0
  • pids.max: 當前cgroup及其全部子孫cgroup中所容許建立的總的最大進程數量,在根cgroup下沒有這個文件,緣由顯而易見,由於咱們沒有必要限制整個系統所能建立的進程數量。server

    #max表示沒作任何限制
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max 
    max

限制進程數

這裏咱們演示一下如何讓限制功能生效

#--------------------------第一個shell窗口----------------------
#將pids.max設置爲1,即當前cgroup只容許有一個進程
dev@dev:/sys/fs/cgroup/pids/test$ echo 1 > pids.max
#將當前bash進程加入到該cgroup
dev@dev:/sys/fs/cgroup/pids/test$ echo $$ > cgroup.procs
#--------------------------第二個shell窗口----------------------
#從新打開一個bash窗口,在裏面看看cgroup 「test」裏面的一些數據
#由於這是一個新開的bash,跟cgroup 」test「沒有任何關係,因此在這裏運行命令不會影響cgroup 「test」
dev@dev:~$ cd /sys/fs/cgroup/pids/test
#設置的最大進程數是1
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
1
#目前test裏面已經有了一個進程,說明不能在fork或者clone進程了
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
1
#這個進程就是第一個窗口的bash
dev@dev:/sys/fs/cgroup/pids/test$ cat cgroup.procs
3083
#--------------------------第一個shell窗口----------------------
#回到第一個窗口,隨便運行一個命令,因爲當前pids.current已經等於pids.max了,
#因此建立新進程失敗,因而命令運行失敗,說明限制生效
dev@dev:/sys/fs/cgroup/pids/test$ ls
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: Resource temporarily unavailable

當前cgroup和子cgroup之間的關係

當前cgroup中的pids.current和pids.max表明了當前cgroup及全部子孫cgroup的全部進程,因此子孫cgroup中的pids.max大小不能超過父cgroup中的大小,若是子cgroup中的pids.max設置的大於父cgroup裏的大小,會怎麼樣?請看下面的演示

#繼續使用上面的兩個窗口
#--------------------------第二個shell窗口----------------------
#將pids.max設置成2
dev@dev:/sys/fs/cgroup/pids/test$ echo 2 > pids.max
#在test下面建立一個子cgroup
dev@dev:/sys/fs/cgroup/pids/test$ mkdir subtest
dev@dev:/sys/fs/cgroup/pids/test$ cd subtest/
#將subtest的pids.max設置爲5
dev@dev:/sys/fs/cgroup/pids/test/subtest$ echo 5 > pids.max
#將當前bash進程加入到subtest中
dev@dev:/sys/fs/cgroup/pids/test/subtest$ echo $$ > cgroup.procs
#--------------------------第三個shell窗口----------------------
#從新打開一個bash窗口,看一下test和subtest裏面的數據
#test裏面的數據以下:
dev@dev:~$ cd /sys/fs/cgroup/pids/test
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
2
#這裏爲2表示目前test和subtest裏面總的進程數爲2
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
2
dev@dev:/sys/fs/cgroup/pids/test$ cat cgroup.procs
3083

#subtest裏面的數據以下:
dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/pids.max
5
dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/pids.current
1
dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/cgroup.procs
3185
#--------------------------第一個shell窗口----------------------
#回到第一個窗口,隨便運行一個命令,因爲test裏面的pids.current已經等於pids.max了,
#因此建立新進程失敗,因而命令運行失敗,說明限制生效
dev@dev:/sys/fs/cgroup/pids/test$ ls
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: Resource temporarily unavailable
#--------------------------第二個shell窗口----------------------
#回到第二個窗口,隨便運行一個命令,雖然subtest裏面的pids.max還大於pids.current,
#但因爲其父cgroup 「test」裏面的pids.current已經等於pids.max了,
#因此建立新進程失敗,因而命令運行失敗,說明子cgroup中的進程數不只受本身的pids.max的限制,
#還受祖先cgroup的限制
dev@dev:/sys/fs/cgroup/pids/test/subtest$ ls
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: Resource temporarily unavailable

pids.current > pids.max的狀況

並非全部狀況下都是pids.max >= pids.current,在下面兩種狀況下,會出現pids.max < pids.current 的狀況:

  • 設置pids.max時,將其值設置的比pids.current小

    #繼續使用上面的三個窗口
    #--------------------------第三個shell窗口----------------------
    #將test的pids.max設置爲1
    dev@dev:/sys/fs/cgroup/pids/test$ echo 1 > pids.max
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    #這個時候就會出現pids.current > pids.max的狀況
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
    2
    
    #--------------------------第一個shell窗口----------------------
    #回到第一個shell
    #仍是運行失敗,說明雖然pids.current > pids.max,但限制建立新進程的功能仍是會生效
    dev@dev:/sys/fs/cgroup/pids/test$ ls
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: Resource temporarily unavailable
  • pids.max只會在當前cgroup中的進程fork、clone的時候生效,將其餘進程加入到當前cgroup時,不會檢測pids.max,因此將其餘進程加入到當前cgroup有可能會致使pids.current > pids.max

    #繼續使用上面的三個窗口
    #--------------------------第三個shell窗口----------------------
    #將subtest中的進程移動到根cgroup下,而後刪除subtest
    dev@dev:/sys/fs/cgroup/pids/test$ sudo sh -c 'echo 3185 > /sys/fs/cgroup/pids/cgroup.procs'
    #裏面沒有進程了,說明移動成功
    dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/cgroup.procs
    #移除成功
    dev@dev:/sys/fs/cgroup/pids/test$ rmdir subtest/
    
    #這時候test下的pids.max等於pids.current了
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
    1
    
    #--------------------------第二個shell窗口----------------------
    #將當前bash加入到test中
    dev@dev:/sys/fs/cgroup/pids/test/subtest$ cd ..
    dev@dev:/sys/fs/cgroup/pids/test$ echo $$ > cgroup.procs
    
    #--------------------------第三個shell窗口----------------------
    #回到第三個窗口,查看相關信息
    #第一個和第二個窗口的bash都屬於test
    dev@dev:/sys/fs/cgroup/pids/test$ cat cgroup.procs
    3083
    3185
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    #出現了pids.current > pids.max的狀況,這是由於咱們將第二個窗口的shell加入了test
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
    2
    #--------------------------第二個shell窗口----------------------
    #對fork調用的限制仍然生效
    dev@dev:/sys/fs/cgroup/pids/test$ ls
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: Resource temporarily unavailable

清理

#--------------------------第三個shell窗口----------------------
dev@dev:/sys/fs/cgroup/pids/test$ sudo sh -c 'echo 3185 > /sys/fs/cgroup/pids/cgroup.procs'
dev@dev:/sys/fs/cgroup/pids/test$ sudo sh -c 'echo 3083 > /sys/fs/cgroup/pids/cgroup.procs'
dev@dev:/sys/fs/cgroup/pids/test$ cd ..
dev@dev:/sys/fs/cgroup/pids$ sudo rmdir test/

結束語

本文介紹瞭如何利用pids這個subsystem來限制cgroup中的進程數,以及一些要注意的地方,總的來講pids比較簡單。下一篇將介紹稍微複雜點的內存控制。

參考

相關文章
相關標籤/搜索