Linux Cgroup系列(04):限制cgroup的內存使用(subsystem之memory)

有了上一篇關於pids的熱身以後,咱們這篇將介紹稍微複雜點的內存控制。html

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

爲何須要內存控制?

代碼總會有bug,有時會有內存泄漏,或者有意想不到的內存分配狀況,或者這是個惡意程序,運行起來就是爲了榨乾系統內存,讓其它進程沒法分配到足夠的內存而出現異常,若是系統配置了交換分區,會致使系統大量使用交換分區,從而系統運行很慢。linux

  • 站在一個普通Linux開發者的角度,若是能控制一個或者一組進程所能使用的內存數,那麼就算代碼有bug,內存泄漏也不會對系統形成影響,由於能夠設置內存使用量的上限,當到達這個值以後能夠將進程重啓。web

  • 站在一個系統管理者的角度,若是能限制每組進程所能使用的內存量,那麼無論程序的質量如何,都能將它們對系統的影響降到最低,從而保證整個系統的穩定性。shell

內存控制能控制些什麼?

  • 限制cgroup中全部進程所能使用的物理內存總量ubuntu

  • 限制cgroup中全部進程所能使用的物理內存+交換空間總量(CONFIG_MEMCG_SWAP): 通常在server上,不太會用到swap空間,因此不在這裏介紹這部份內容。segmentfault

  • 限制cgroup中全部進程所能使用的內核內存總量及其它一些內核資源(CONFIG_MEMCG_KMEM): 限制內核內存有什麼用呢?其實限制內核內存就是限制當前cgroup所能使用的內核資源,好比進程的內核棧空間,socket所佔用的內存空間等,經過限制內核內存,當內存吃緊時,能夠阻止當前cgroup繼續建立進程以及向內核申請分配更多的內核資源。因爲這塊功能被使用的較少,本篇中也不對它作介紹。bash

內核相關的配置

  • 因爲memory subsystem比較耗資源,因此內核專門添加了一個參數cgroup_disable=memory來禁用整個memory subsystem,這個參數能夠經過GRUB在啓動系統的時候傳給內核,加了這個參數後內核將再也不進行memory subsystem相關的計算工做,在系統中也不能掛載memory subsystem。app

  • 上面提到的CONFIG_MEMCG_SWAP和CONFIG_MEMCG_KMEM都是擴展功能,在使用前請確認當前內核是否支持,下面看看ubuntu 16.04的內核:socket

    #這裏CONFIG_MEMCG_SWAP和CONFIG_MEMCG_KMEM等於y表示內核已經編譯了該模塊,即支持相關功能
    dev@dev:~$ cat /boot/config-`uname -r`|grep CONFIG_MEMCG
    CONFIG_MEMCG=y
    CONFIG_MEMCG_SWAP=y
    # CONFIG_MEMCG_SWAP_ENABLED is not set
    CONFIG_MEMCG_KMEM=y
  • CONFIG_MEMCG_SWAP控制內核是否支持Swap Extension,而CONFIG_MEMCG_SWAP_ENABLED(3.6之後的內核新加的參數)控制默認狀況下是否使用Swap Extension,因爲Swap Extension比較耗資源,因此不少發行版(好比ubuntu)默認狀況下會禁用該功能(這也是上面那行被註釋掉的緣由),固然用戶也能夠根據實際狀況,經過設置內核參數swapaccount=0或者1來手動禁用和啓用Swap Extension。

怎麼控制?

在ubuntu 16.04裏面,systemd已經幫咱們將memory綁定到了/sys/fs/cgroup/memory

#若是這裏發現有多行結果,說明這顆cgroup數被綁定到了多個地方,
#不過不要擔憂,因爲它們都是指向同一顆cgroup樹,因此它們裏面的內容是如出一轍的
dev@dev:~$ mount|grep memory
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

建立子cgroup

在/sys/fs/cgroup/memory下建立一個子目錄即建立了一個子cgroup

#--------------------------第一個shell窗口----------------------
dev@dev:~$ cd /sys/fs/cgroup/memory
dev@dev:/sys/fs/cgroup/memory$ sudo mkdir test
dev@dev:/sys/fs/cgroup/memory$ ls test
cgroup.clone_children  memory.kmem.failcnt             memory.kmem.tcp.limit_in_bytes      memory.max_usage_in_bytes        memory.soft_limit_in_bytes  notify_on_release
cgroup.event_control   memory.kmem.limit_in_bytes      memory.kmem.tcp.max_usage_in_bytes  memory.move_charge_at_immigrate  memory.stat                 tasks
cgroup.procs           memory.kmem.max_usage_in_bytes  memory.kmem.tcp.usage_in_bytes      memory.numa_stat                 memory.swappiness
memory.failcnt         memory.kmem.slabinfo            memory.kmem.usage_in_bytes          memory.oom_control               memory.usage_in_bytes
memory.force_empty     memory.kmem.tcp.failcnt         memory.limit_in_bytes               memory.pressure_level            memory.use_hierarchy

從上面ls的輸出能夠看出,除了每一個cgroup都有的那幾個文件外,和memory相關的文件還很多(因爲ubuntu默認禁用了CONFIG_MEMCG_SWAP,因此這裏看不到swap相關的文件),這裏先作個大概介紹(kernel相關的文件除外),後面會詳細介紹每一個文件的做用

cgroup.event_control       #用於eventfd的接口
 memory.usage_in_bytes      #顯示當前已用的內存
 memory.limit_in_bytes      #設置/顯示當前限制的內存額度
 memory.failcnt             #顯示內存使用量達到限制值的次數
 memory.max_usage_in_bytes  #歷史內存最大使用量
 memory.soft_limit_in_bytes #設置/顯示當前限制的內存軟額度
 memory.stat                #顯示當前cgroup的內存使用狀況
 memory.use_hierarchy       #設置/顯示是否將子cgroup的內存使用狀況統計到當前cgroup裏面
 memory.force_empty         #觸發系統當即儘量的回收當前cgroup中能夠回收的內存
 memory.pressure_level      #設置內存壓力的通知事件,配合cgroup.event_control一塊兒使用
 memory.swappiness          #設置和顯示當前的swappiness
 memory.move_charge_at_immigrate #設置當進程移動到其餘cgroup中時,它所佔用的內存是否也隨着移動過去
 memory.oom_control         #設置/顯示oom controls相關的配置
 memory.numa_stat           #顯示numa相關的內存

參考:eventfdnuma

添加進程

「建立並管理cgroup」中介紹的同樣,往cgroup中添加進程只要將進程號寫入cgroup.procs就能夠了

注意:本篇將以進程爲單位進行操做,不考慮以線程爲單位進行管理(緣由見「建立並管理cgroup」中cgroup.pro與tasks的區別),也即只寫cgroup.procs文件,不會寫tasks文件

#--------------------------第二個shell窗口----------------------
#從新打開一個shell窗口,避免相互影響
dev@dev:~$ cd /sys/fs/cgroup/memory/test/
dev@dev:/sys/fs/cgroup/memory/test$ echo $$
4589
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo $$ >> cgroup.procs"
#運行top命令,這樣這個cgroup消耗的內存會多點,便於觀察
dev@dev:/sys/fs/cgroup/memory/test$ top
#後續操做再也不在這個窗口進行,避免在這個bash中運行進程影響cgropu裏面的進程數及相關統計

設置限額

設置限額很簡單,寫文件memory.limit_in_bytes就能夠了,請仔細看示例

#--------------------------第一個shell窗口----------------------
#回到第一個shell窗口
dev@dev:/sys/fs/cgroup/memory$ cd test
#這裏兩個進程id分別時第二個窗口的bash和top進程
dev@dev:/sys/fs/cgroup/memory/test$ cat cgroup.procs
4589
4664
#開始設置以前,看看當前使用的內存數量,這裏的單位是字節
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.usage_in_bytes
835584

#設置1M的限額
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 1M > memory.limit_in_bytes"
#設置完以後記得要查看一下這個文件,由於內核要考慮頁對齊, 因此生效的數量不必定徹底等於設置的數量
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.limit_in_bytes
1048576

#若是再也不須要限制這個cgroup,寫-1到文件memory.limit_in_bytes便可
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo -1 > memory.limit_in_bytes"
#這時能夠看到limit被設置成了一個很大的數字
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.limit_in_bytes
9223372036854771712

若是設置的限額比當前已經使用的內存少呢?如上面顯示當前bash用了800多k,若是我設置limit爲400K會怎麼樣?

#--------------------------第一個shell窗口----------------------
#先用free看下當前swap被用了多少
dev@dev:/sys/fs/cgroup/memory/test$ free
              total        used        free      shared  buff/cache   available
Mem:         500192       45000       34200        2644      420992      424020
Swap:        524284          16      524268
#設置內存限額爲400K
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 400K > memory.limit_in_bytes"

#再看當前cgroup的內存使用狀況
#發現內存佔用少了不少,恰好在400K之內,原來用的那些內存都去哪了呢?
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.usage_in_bytes
401408

#再看swap空間的佔用狀況,和剛開始比,多了500-16=384K,說明內存中的數據被移到了swap上
dev@dev:/sys/fs/cgroup/memory/test$ free
              total        used        free      shared  buff/cache   available
Mem:         500192       43324       35132        2644      421736      425688
Swap:        524284         500      523784

#這個時候再來看failcnt,發現有453次之多(隔幾秒再看這個文件,發現次數在增加)
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.failcnt
453

#再看看memory.stat(這裏只顯示部份內容),發現物理內存用了400K,
#但有不少pgmajfault以及pgpgin和pgpgout,說明發生了不少的swap in和swap out
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.stat
rss 409600
total_pgpgin 4166
total_pgpgout 4066
total_pgfault 7558
total_pgmajfault 419

#從上面的結果能夠看出,當物理內存不夠時,就會觸發memory.failcnt裏面的數量加1,
#但進程不會被kill掉,那是由於內核會嘗試將物理內存中的數據移動到swap空間中,從而讓內存分配成功

若是設置的限額太小,就算swap out部份內存後仍是不夠會怎麼樣?

#--------------------------第一個shell窗口----------------------
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 1K > memory.limit_in_bytes"
#進程已經不在了(第二個窗口已經掛掉了)
dev@dev:/sys/fs/cgroup/memory/test$ cat cgroup.procs
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.usage_in_bytes
0
#從這裏的結果能夠看出,第二個窗口的bash和top都被kill掉了

從上面的這些測試能夠看出,一旦設置了內存限制,將當即生效,而且當物理內存使用量達到limit的時候,memory.failcnt的內容會加1,但這時進程不必定就會被kill掉,內核會盡可能將物理內存中的數據移到swap空間上去,若是實在是沒辦法移動了(設置的limit太小,或者swap空間不足),默認狀況下,就會kill掉cgroup裏面繼續申請內存的進程。

觸發控制

當物理內存達到上限後,系統的默認行爲是kill掉cgroup中繼續申請內存的進程,那麼怎麼控制這樣的行爲呢?答案是配置memory.oom_control

這個文件裏面包含了一個控制是否爲當前cgroup啓動OOM-killer的標識。若是寫0到這個文件,將啓動OOM-killer,當內核沒法給進程分配足夠的內存時,將會直接kill掉該進程;若是寫1到這個文件,表示不啓動OOM-killer,當內核沒法給進程分配足夠的內存時,將會暫停該進程直到有空餘的內存以後再繼續運行;同時,memory.oom_control還包含一個只讀的under_oom字段,用來表示當前是否已經進入oom狀態,也便是否有進程被暫停了。

注意:root cgroup的oom killer是不能被禁用的

爲了演示OOM-killer的功能,建立了下面這樣一個程序,用來向系統申請內存,它會每秒消耗1M的內存。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MB (1024 * 1024)

int main(int argc, char *argv[])
{
    char *p;
    int i = 0;
    while(1) {
        p = (char *)malloc(MB);
        memset(p, 0, MB);
        printf("%dM memory allocated\n", ++i);
        sleep(1);
    }

    return 0;
}

保存上面的程序到文件~/mem-allocate.c,而後編譯並測試

#--------------------------第一個shell窗口----------------------
#編譯上面的文件
dev@dev:/sys/fs/cgroup/memory/test$ gcc ~/mem-allocate.c -o ~/mem-allocate
#設置內存限額爲5M
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 5M > memory.limit_in_bytes"
#將當前bash加入到test中,這樣這個bash建立的全部進程都會自動加入到test中
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo $$ >> cgroup.procs"
#默認狀況下,memory.oom_control的值爲0,即默認啓用oom killer
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.oom_control
oom_kill_disable 0
under_oom 0
#爲了不受swap空間的影響,設置swappiness爲0來禁止當前cgroup使用swap
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 0 > memory.swappiness"
#當分配第5M內存時,因爲總內存量超過了5M,因此進程被kill了
dev@dev:/sys/fs/cgroup/memory/test$ ~/mem-allocate
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
Killed

#設置oom_control爲1,這樣內存達到限額的時候會暫停
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 1 >> memory.oom_control"
#跟預期的同樣,程序被暫停了
dev@dev:/sys/fs/cgroup/memory/test$ ~/mem-allocate
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated

#--------------------------第二個shell窗口----------------------
#再打開一個窗口
dev@dev:~$ cd /sys/fs/cgroup/memory/test/
#這時候能夠看到memory.oom_control裏面under_oom的值爲1,表示當前已經oom了
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.oom_control
oom_kill_disable 1
under_oom 1
#修改test的額度爲7M
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 7M > memory.limit_in_bytes"

#--------------------------第一個shell窗口----------------------
#再回到第一個窗口,會發現進程mem-allocate繼續執行了兩步,而後暫停在6M那裏了
dev@dev:/sys/fs/cgroup/memory/test$ ~/mem-allocate
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
5M memory allocated
6M memory allocated

該文件還能夠配合cgroup.event_control實現OOM的通知,當OOM發生時,能夠收到相關的事件,下面是用於測試的程序,流程大概以下:

  1. 利用函數eventfd()建立一個efd;

  2. 打開文件memory.oom_control,獲得ofd;

  3. 往cgroup.event_control中寫入這麼一串:<efd> <ofd>

  4. 經過讀efd獲得通知,而後打印一句話到終端

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/eventfd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static inline void die(const char *msg)
{
    fprintf(stderr, "error: %s: %s(%d)\n", msg, strerror(errno), errno);
    exit(EXIT_FAILURE);
}

#define BUFSIZE 256

int main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    int efd, cfd, ofd;
    uint64_t u;

    if ((efd = eventfd(0, 0)) == -1)
        die("eventfd");

    snprintf(buf, BUFSIZE, "%s/%s", argv[1], "cgroup.event_control");
    if ((cfd = open(buf, O_WRONLY)) == -1)
        die("cgroup.event_control");

    snprintf(buf, BUFSIZE, "%s/%s", argv[1], "memory.oom_control");
    if ((ofd = open(buf, O_RDONLY)) == -1)
        die("memory.oom_control");

    snprintf(buf, BUFSIZE, "%d %d", efd, ofd);
    if (write(cfd, buf, strlen(buf)) == -1)
        die("write cgroup.event_control");

    if (close(cfd) == -1)
        die("close cgroup.event_control");

    for (;;) {
        if (read(efd, &u, sizeof(uint64_t)) != sizeof(uint64_t))
            die("read eventfd");
        printf("mem_cgroup oom event received\n");
    }

    return 0;
}

將上面的文件保存爲~/oom_listen.c,而後測試以下

#--------------------------第二個shell窗口----------------------
#編譯程序
dev@dev:/sys/fs/cgroup/memory/test$ gcc ~/oom_listen.c -o ~/oom_listen
#啓用oom killer
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 0 >> memory.oom_control"
#設置限額爲2M,縮短測試周期
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 2M > memory.limit_in_bytes"
#啓動監聽程序
dev@dev:/sys/fs/cgroup/memory/test$ ~/oom_listen /sys/fs/cgroup/memory/test

#--------------------------第一個shell窗口----------------------
#連續運行兩次mem-allocate,使它觸發oom killer
dev@dev:/sys/fs/cgroup/memory/test$ ~/mem-allocate
1M memory allocated
Killed
dev@dev:/sys/fs/cgroup/memory/test$ ~/mem-allocate
1M memory allocated
Killed

#--------------------------第二個shell窗口----------------------
#回到第二個窗口能夠看到,收到了兩次oom事件
dev@dev:/sys/fs/cgroup/memory/test$ ~/oom_listen /sys/fs/cgroup/memory/test
mem_cgroup oom event received
mem_cgroup oom event received

其餘

進程遷移(migration)

當一個進程從一個cgroup移動到另外一個cgroup時,默認狀況下,該進程已經佔用的內存仍是統計在原來的cgroup裏面,不會佔用新cgroup的配額,但新分配的內存會統計到新的cgroup中(包括swap out到交換空間後再swap in到物理內存中的部分)。

咱們能夠經過設置memory.move_charge_at_immigrate讓進程所佔用的內存隨着進程的遷移一塊兒遷移到新的cgroup中。

enable: echo 1 > memory.move_charge_at_immigrate
disable:echo 0 > memory.move_charge_at_immigrate

注意: 就算設置爲1,但若是不是thread group的leader,這個task佔用的內存也不能被遷移過去。換句話說,若是以線程爲單位進行遷移,必須是進程的第一個線程,若是以進程爲單位進行遷移,就沒有這個問題。

當memory.move_charge_at_immigrate被設置成1以後,進程佔用的內存將會被統計到目的cgroup中,若是目的cgroup沒有足夠的內存,系統將嘗試回收目的cgroup的部份內存(和系統內存緊張時的機制同樣,刪除不經常使用的file backed的內存或者swap out到交換空間上,請參考Linux內存管理),若是回收不成功,那麼進程遷移將失敗。

注意:遷移內存佔用數據是比較耗時的操做。

移除cgroup

當memory.move_charge_at_immigrate爲0時,就算當前cgroup中裏面的進程都已經移動到其它cgropu中去了,因爲進程已經佔用的內存沒有被統計過去,當前cgroup有可能還佔用不少內存,當移除該cgroup時,佔用的內存須要統計到誰頭上呢?答案是依賴memory.use_hierarchy的值,若是該值爲0,將會統計到root cgroup裏;若是值爲1,將統計到它的父cgroup裏面。

force_empty

當向memory.force_empty文件寫入0時(echo 0 > memory.force_empty),將會當即觸發系統儘量的回收該cgroup佔用的內存。該功能主要使用場景是移除cgroup前(cgroup中沒有進程),先執行該命令,能夠儘量的回收該cgropu佔用的內存,這樣遷移內存的佔用數據到父cgroup或者root cgroup時會快些。

memory.swappiness

該文件的值默認和全局的swappiness(/proc/sys/vm/swappiness)同樣,修改該文件只對當前cgroup生效,其功能和全局的swappiness同樣,請參考Linux交換空間中關於swappiness的介紹。

注意:有一點和全局的swappiness不一樣,那就是若是這個文件被設置成0,就算系統配置的有交換空間,當前cgroup也不會使用交換空間。

memory.use_hierarchy

該文件內容爲0時,表示不使用繼承,即父子cgroup之間沒有關係;當該文件內容爲1時,子cgroup所佔用的內存會統計到全部祖先cgroup中。

若是該文件內容爲1,當一個cgroup內存吃緊時,會觸發系統回收它以及它全部子孫cgroup的內存。

注意: 當該cgroup下面有子cgroup或者父cgroup已經將該文件設置成了1,那麼當前cgroup中的該文件就不能被修改。

#當前cgroup和父cgroup裏都是1
dev@dev:/sys/fs/cgroup/memory/test$ cat memory.use_hierarchy
1
dev@dev:/sys/fs/cgroup/memory/test$ cat ../memory.use_hierarchy
1

#因爲父cgroup裏面的值爲1,因此修改當前cgroup的值失敗
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 0 > ./memory.use_hierarchy"
sh: echo: I/O error

#因爲父cgroup裏面有子cgroup(至少有當前cgroup這麼一個子cgroup),
#修改父cgroup裏面的值也失敗
dev@dev:/sys/fs/cgroup/memory/test$ sudo sh -c "echo 0 > ../memory.use_hierarchy"
sh: echo: I/O error

memory.soft_limit_in_bytes

有了hard limit(memory.limit_in_bytes),爲何還要soft limit呢?hard limit是一個硬性標準,絕對不能超過這個值,而soft limit能夠被超越,既然能被超越,要這個配置還有啥用?先看看它的特色

  1. 當系統內存充裕時,soft limit不起任何做用

  2. 當系統內存吃緊時,系統會盡可能的將cgroup的內存限制在soft limit值之下(內核會盡可能,但不100%保證)

從它的特色能夠看出,它的做用主要發生在系統內存吃緊時,若是沒有soft limit,那麼全部的cgroup一塊兒競爭內存資源,佔用內存多的cgroup不會讓着內存佔用少的cgroup,這樣就會出現某些cgroup內存飢餓的狀況。若是配置了soft limit,那麼當系統內存吃緊時,系統會讓超過soft limit的cgroup釋放出超過soft limit的那部份內存(有可能更多),這樣其它cgroup就有了更多的機會分配到內存。

從上面的分析看出,這實際上是系統內存不足時的一種妥協機制,給次等重要的進程設置soft limit,當系統內存吃緊時,把機會讓給其它重要的進程。

注意: 當系統內存吃緊且cgroup達到soft limit時,系統爲了把當前cgroup的內存使用量控制在soft limit下,在收到當前cgroup新的內存分配請求時,就會觸發回收內存操做,因此一旦到達這個狀態,就會頻繁的觸發對當前cgroup的內存回收操做,會嚴重影響當前cgroup的性能。

memory.pressure_level

這個文件主要用來監控當前cgroup的內存壓力,當內存壓力大時(即已使用內存快達到設置的限額),在分配內存以前須要先回收部份內存,從而影響內存分配速度,影響性能,而經過監控當前cgroup的內存壓力,能夠在有壓力的時候採起必定的行動來改善當前cgroup的性能,好比關閉當前cgroup中不重要的服務等。目前有三種壓力水平:

low

意味着系統在開始爲當前cgroup分配內存以前,須要先回收內存中的數據了,這時候回收的是在磁盤上有對應文件的內存數據。

medium

意味着系統已經開始頻繁爲當前cgroup使用交換空間了。

critical

快撐不住了,系統隨時有可能kill掉cgroup中的進程。

如何配置相關的監聽事件呢?和memory.oom_control相似,大概步驟以下:

  1. 利用函數eventfd(2)建立一個event_fd

  2. 打開文件memory.pressure_level,獲得pressure_level_fd

  3. 往cgroup.event_control中寫入這麼一串:<event_fd> <pressure_level_fd> <level>

  4. 而後經過讀event_fd獲得通知

注意: 多個level可能要建立多個event_fd,好像沒有辦法共用一個(本人沒有測試過)

Memory thresholds

咱們能夠經過cgroup的事件通知機制來實現對內存的監控,當內存使用量穿過(變得高於或者低於)咱們設置的值時,就會收到通知。使用方法和memory.oom_control相似,大概步驟以下:

  1. 利用函數eventfd(2)建立一個event_fd

  2. 打開文件memory.usage_in_bytes,獲得usage_in_bytes_fd

  3. 往cgroup.event_control中寫入這麼一串:<event_fd> <usage_in_bytes_fd> <threshold>

  4. 而後經過讀event_fd獲得通知

stat file

這個文件包含的統計項比較細,須要一些內核的內存管理知識才能看懂,這裏就不介紹了(怕說錯)。詳細信息能夠參考Memory Resource Controller中的「5.2 stat file」。這裏有幾個須要注意的地方:

  • 裏面total開頭的統計項包含了子cgroup的數據(前提條件是memory.use_hierarchy等於1)。

  • 裏面的'rss + file_mapped"才約等因而咱們常說的RSS(ps aux命令看到的RSS)

  • 文件(動態庫和可執行文件)及共享內存能夠在多個進程之間共享,不過它們只會統計到他們的owner cgroup中的file_mapped去。(不肯定是怎麼定義owner的,但若是看到當前cgroup的file_mapped值很小,說明共享的數據沒有算到它頭上,而是其它的cgroup)

結束語

本篇沒有介紹swap和kernel相關的內容,不過在實際使用過程當中必定要留意swap空間,若是系統使用了交換空間,那麼設置限額時必定要注意一點,那就是當cgroup的物理空間不夠時,內核會將不經常使用的內存swap out到交換空間上,從而致使一直不觸發oom killer,而是不停的swap out/in,致使cgroup中的進程運行速度很慢。若是必定要用交換空間,最好的辦法是限制swap+物理內存的額度,雖然咱們在這篇中沒有介紹這部份內容,但其使用方法和限制物理內存是同樣的,只是換作寫文件memory.memsw.limit_in_bytes罷了。

參考

相關文章
相關標籤/搜索