Linux Cgroup v1(中文翻譯)(6):Memory Resource Controller

来源:https://www.cnblogs.com/aosp/archive/2022/06/16/16380743.html
-Advertisement-
Play Games

Memory Resource Controller 記憶體資源控制器 註意: 這個文檔完完全全地過時了,需要整個地重寫。但它還是包含了有用的信息,所以我們仍舊把它保留在這裡,但是如果你需要深入理解的話,需要確保核對過當前的代碼。 註意: 記憶體資源控制器在本文檔中指的是記憶體控制器。不要混淆了這裡記憶體控 ...


Memory Resource Controller

記憶體資源控制器


目錄


註意:
這個文檔完完全全地過時了,需要整個地重寫。但它還是包含了有用的信息,所以我們仍舊把它保留在這裡,但是如果你需要深入理解的話,需要確保核對過當前的代碼。

註意:
記憶體資源控制器在本文檔中指的是記憶體控制器。不要混淆了這裡記憶體控制器和硬體中使用的記憶體控制器。

(給編輯者)在本文檔中:
當我們提到記憶體控制器的控制組(cgroupfs目錄)時,我們稱之為“記憶體控制組”(memory cgroup)。當你查看git-log和源代碼時,你會看到補丁的抬頭和功能名稱傾向於使用"memcg"。在本文檔中,我們避免使用它。

記憶體控制器的優點和目的

記憶體控制器把一組任務的記憶體從系統中其他任務的記憶體中隔離出來。LWN[12]中的文章提到了記憶體控制器的一些作用。記憶體控制器能用來:

  • 分離應用程式,或者一組記憶體饑餓的應用程式能夠被分離和限制到較少量的記憶體上。
  • 創建記憶體數量有限的控制組。也可選擇啟動時帶有mem=xxx選項。
  • 虛擬化方案能控制分配給虛擬機實例的記憶體數量。
  • CD/DVD燒錄器能控制系統其他部分使用的記憶體數量以確保燒錄不會因為缺少記憶體而失敗。
  • 還有幾個其他用例。查找或者使用控制器只是為了好玩(學習和hack進虛擬機)。

當前狀態: linux-2.6.34-mmotm(development version of 2010/April)。

功能特性:

  • 統計和限制匿名頁面、文件緩存、交換緩存的用量。
  • 頁面能排他性地連接到per-memcg LRU,而不是全局的LRU。
  • 任意地統計和限制memory+swap的用量。
  • 層次結構化的統計。
  • 軟限制
  • moving (recharging) account at moving a task is selectable.
  • 用量閾值通知器。
  • 記憶體壓力通知器
  • oom-killer禁用開關和oom-notifier
  • 跟控制組無限控制。

內核記憶體支持是一個正在進行的工作,當前版本提供了基本功能(2.7節)。

控制文件概要

名稱 說明
tasks 綁定任務(線程)並現實線程列表
cgroup.procs 顯示進程列表
cgroup.event_control event_fd()的介面, CONFIG_PREEMPT_RT系統不可用
memory.usage_in_bytes 當前記憶體用量
memory.memsw.usage_in_bytes 當前memory+Swap用量
memory.limit_in_bytes 設置/顯示記憶體用量限制
memory.memsw.limit_in_bytes 設置/顯示memory+Swap用量限制
memory.failcnt 顯示記憶體用量觸達閾值的次數
memory.memsw.failcnt 顯示memory+Swap用量觸達閾值的次數
memory.max_usage_in_bytes 顯示記錄的最大記憶體用量
memory.memsw.max_usage_in_bytes 顯示記錄的最大memory+Swap用量
memory.soft_limit_in_bytes 設置/顯示記憶體用量軟限制, CONFIG_PREEMPT_RT系統不可用
memory.stat 顯示狀態統計
memory.use_hierarchy 設置/顯示分層數量,已過時不要使用
memory.force_empty 觸發強制頁面回收
memory.pressure_level 設置記憶體壓力通知
memory.swappiness 設置/顯示vmscan的swappiness參數
memory.move_charge_at_immigrate 設置/顯示moving charges控制
memory.oom_control 設置/顯示oom控制
memory.numa_stat 顯示每個numa節點的記憶體使用數量
memory.kmem.limit_in_bytes 已過時
memory.kmem.usage_in_bytes 顯示內核記憶體分配用量
memory.kmem.failcnt 顯示內核記憶體用量觸達限制的次數
memory.kmem.max_usage_in_bytes 顯示記錄的最大內核記憶體用量
memory.kmem.tcp.limit_in_bytes 設置/顯示TCP緩衝記憶體的硬限制
memory.kmem.tcp.usage_in_bytes 顯示TCP緩衝記憶體分配用量
memory.kmem.tcp.failcnt 顯示TCP緩衝記憶體用量觸達限制的次數
memory.kmem.tcp.max_usage_in_bytes 顯示記錄的最大TCP緩衝記憶體用量

1. 歷史

記憶體控制器有一段很長的歷史。記憶體控制器的需求描述是Balbir Singh [1]發表的。同時RFC發佈了記憶體控制的幾個實現方式。RFC的目標是為記憶體控制的最小化需求特性構建共識和認可。西一個RSS控制器被Balbir Singh[2]在2007年2月發表。Pavel Emelianov [3][4][5]又發表了3個版本的RSS控制器。在OLS,在資源管理BoF,每個人都建議我們頁面緩存和RSS都一起處理。另一個需求被提出來允許用戶空間處理OOM。現在的記憶體控制器是版本6,它結合了RSS和頁面緩存控制[11]。

2. 記憶體控制

記憶體是一種數量有限的獨特資源。如果任務請求需求CPU來處理,任務能通過小時/天/月或者年的周期來擴展它的處理能力,但是記憶體只能被覆用來完成任務。
記憶體控制器的實現已經被劃分成多個階段,他們是:

  1. Memory controller
  2. mlock(2) controller
  3. Kernel user memory accounting and slab control
  4. user mappings length controller

記憶體控制器是第一個已經被開發出來的控制器。

2.1. 設計

設計的核心是一個名叫page_counter的計數器。page_counter跟蹤控制器相關的進程組的當前記憶體用量和限制。每個控制組有記憶體控制器相關的數據結構(mem_cgroup)。

2.2. 統計

     +--------------------+
     |  mem_cgroup        |
     |  (page_counter)    |
     +--------------------+
      /            ^      \
     /             |       \
+---------------+  |        +---------------+
| mm_struct     |  |....    | mm_struct     |
|               |  |        |               |
+---------------+  |        +---------------+
                   |
                   + --------------+
                                   |
+---------------+           +------+--------+
| page          +---------->  page_cgroup|
|               |           |               |
+---------------+           +---------------+
(Figure 1: Hierarchy of Accounting)

圖1顯示了控制器的幾個重要方面:

  1. 以控制組(per cgroup)為單位進行統計
  2. 每個mm_struct知道它屬於哪個cgroup
  3. 每個頁面有一個指針指向page_cgroup,它知道cgroup屬於哪個控制組。

統計按照如下方式完成:mem_cgroup_charge_common() 被調用來構建必須的數據結構,檢查正在記賬的控制組是否超過限制。如果超過,那麼在控制組內回收就會被調用。更多回收詳情參看本文檔的回收章節。如果一切正常,page_cgroup就會更新。page_cgroup在控制組內有他自己的LRU。page_cgroup結構在啟動或者記憶體熱插拔時被分配。

2.2.1 統計詳情

所有映射的anon頁面 (RSS) 和緩存頁面 (Page Cache) 都會被統計。 永遠不可回收的和不會用在LRU上的那些頁面不會被統計。我們只統計通用VM管理下的頁面。

RSS頁面統計在page_fault上,除非他們之前已經統計過。文件頁面插入到inode(radix-tree)時作為Page Cache統計,當它被映射到進程頁表中時,要避免重覆地統計。

完全未映射的RSS頁面是沒有統計的。從radix-tree中移除的PageCache頁面也沒有統計。甚至如果RSS頁面完全沒有被kswapd映射,他們可以作為SwapCache存在以直到被釋放。這些SwapCaches也會被統計。swapped-in頁面在添加到swapcache之後會被統計。

註意: 內核會一次性swapin-readahead和讀取多個swap。從頁面的memcg記錄到swap開始而不管memsw是否被使能,頁面在swapin之後都會被統計。

頁面遷移時,統計信息會被保留。

註意:我們統計pages-on-LRU是因為我們的目的是控制已用頁面的數量,not-on-LRU頁面從VM視角來看是超出控制的。

2.3 共用頁面統計

共用頁面在“第一次觸達頁面方法”(first touch approach)的基礎上統計。第一次觸達頁面的控制組被統計為頁面(?)這種方法背後的原理是使用共用頁面的控制組最後會因此而被記賬。(一旦它未被從控制組中記賬,它將會發生記憶體壓力)。

但是看一下8.2小節:當移動任務到另一個控制組,如果move_charge_at_immigrate被選擇,它的頁面就可以被重新記賬到新的控制組。

2.4 交換記憶體擴展(Swap Extension)

交換記憶體使用總是被記錄。交換記憶體擴展允許讀取和限制它。

當CONFIG_SWAP使能,下列文件就會被增加:

  • memory.memsw.usage_in_bytes
  • memory.memsw.limit_in_bytes

memsw表示memory+swap。memory+swap的用量是由memsw.limit_in_bytes限制的。

例如:假設系統有4G交換記憶體,不小心在2G記憶體限制下分配了6G記憶體,任務將會使用掉所有的交換記憶體。在這種情況下,設置memsw.limit_in_bytes=3G可以防止交換記憶體更壞的使用情況。通過使用memsw限制,你可以避免因為交換記憶體的缺乏而引起的系統OOM。

為什麼是memory+swap而不是swap?
全局LRU(kswapd)能換出任何頁面。換出意味著移動統計從記憶體切到了交換記憶體。而使用memory+swap則不會有任何改變。換句話說,當我們想要限制交換記憶體的用量而不影響全局LRU,從OS視角的觀點來看,memory+swap的限制方式比僅僅限制交換記憶體要更好。

控制組觸達memory.memsw.limit_in_bytes時會發生什麼?
當控制組觸達memory.memsw.limit_in_bytes,在該控制組內做swap-out是沒有用的。swap-out也不可以由控制組程式和已摘除的文件緩存來進行。但是如上所述,為了系統記憶體管理狀態的正常,全局LRU能從控制組換出記憶體。控制組不能阻止它。

2.5 回收Reclaim

每個控制組維護著每個控制組的跟全局VM結構相同的LRU。當控制組超過它的限制,我們首先試著從控制組內回收記憶體以便預留新的空閑頁面。如果回收不成功,OOM程式被調用來殺死控制組內最龐重的任務。(參看第10小節OOM控制)

回收演算法沒有為控制組做修改,被選做回收的頁面來自每個控制組的LRU列表。

註意:
回收不會對根控制組生效,因為我們不能在根控制組上設置任何限制。

註意2:
當panic_on_oom設置為2,整個系統就會panic。
當OOM時間通知器已經註冊,時間就會被髮出。(參看oom_control小節)

2.6 鎖Locking

鎖順序如下:

Page lock (PG_locked bit of page->flags)  
  mm->page_table_lock or split pte_lock  
    lock_page_memcg (memcg->move_lock)  
      mapping->i_pages lock  
        lruvec->lru_lock  

Per-node-per-memcgroup LRU (控制組私有LRU) 由lruvec->lru_lock來守護; 從lruvec->lru_lock分離出頁面之前,PG_lru bit of page->flags會被清除。

2.7 內核記憶體擴展(CONFIG_MEMCG_KMEM)

記憶體控制器能用內核記憶體擴展來限制系統使用的內核記憶體數量。內核記憶體完全不同於用於記憶體,因為它不能被交換出去,這使得它有可能通過消耗太多的這種昂貴資源來Dos系統。

內核記憶體統計對所有記憶體控制組預設為使能的。但是它能在啟動時傳遞cgroup.memory=nokmem選項給內核來禁用。這種情況下內核記憶體根本就不會被統計。

內核記憶體限制對根控制組沒有影響。根控制組的用量可以統計,也可以不用統計。已用的記憶體會被累加到memory.kmem.usage_in_bytes中或者獨立的計數器中。

主kmem計數被傳遞給主計數器,因此kmem統計也可以從用戶計數器里看到。

當前沒有為內核記憶體實現軟限制。當這些限制工作完成之後未來的工作就是出發slab回收。

2.7.1 當前統計的內核記憶體資源

stack pages:
每個進程消耗一些堆棧頁面。通過記賬到內核記憶體,我們可以防止新進程創建太高的內核記憶體。

slab pages:
SLAB或者SLUB分配器分配的頁面都會被跟蹤。每當第一次從memcg內創建緩存時,每個kmem_cache的副本就會創建出來。創建是lazily的,因為當正在創建緩存時有些對象仍然能被跳過。slab頁面內的所有對象應該屬於同一個memcg。當任務在頁面分配期間被移動到不同的memcg時就會失敗。

sockets memory pressure:
有些套接字協議有記憶體壓力閾值。記憶體控制器允許他們分別由每個控制組分別地而不是全局地被控制

tcp memory pressure:
TCP協議的套接字記憶體壓力

2.7.2 常用用例

因為"kmem"計數被傳遞給主用戶計數器,內核記憶體限制絕對不會跟用戶記憶體完全無關。"U"是用戶限制,"K"是內核限制。有三種方式能設置限制:
U != 0, K = unlimited:
這是標準memcg限制機制。內核記憶體完全被忽略。

U != 0, K < U:
內核記憶體是用戶記憶體的子集。在每個控制組記憶體數量被超量分配時這種方式是有用的。超量的內核記憶體限制肯定不推薦,因為box仍然能在不可回收的記憶體之外運行。這種情況下,管理員能設置"K"以便所有控制組總數絕不會超過總的記憶體,隨意地在QOS成本範圍上設置"U"。

警告:
在當前的實現中,記憶體回收不會在觸達K而小於U時被控制組觸發。

U != 0, K >= U:
因為kmem統計被傳遞給用戶計數器,控制組將會觸發兩種記憶體的回收。這種設置給管理員一個統一的記憶體視角。

3. 用戶介面

配置:

  • 使能CONFIG_CGROUPS
  • 使能CONFIG_MEMCG
  • 使能CONFIG_MEMCG_SWAP(來使用swap記憶體擴展)
  • 使能CONFIG_MEMCG_KMEM (來使用kmem記憶體擴展)

準備控制組:

# mount -t tmpfs none /sys/fs/cgroup
# mkdir /sys/fs/cgroup/memory
# mount -t cgroup none /sys/fs/cgroup/memory -o memory

創建新控制組,移入當前bash:

# mkdir /sys/fs/cgroup/memory/0
# echo $$ > /sys/fs/cgroup/memory/0/tasks

此刻開始我們就在0控制組內,更改記憶體限制:

# echo 4M > /sys/fs/cgroup/memory/0/memory.limit_in_bytes

註意:
我們可以使用尾碼(k, K, m, M, g or G)來表示位元組單位值。

註意:
我們能寫"-1"來重置*.limit_in_bytes為無限制。

註意:
我們不能在根控制組下設置任何限制。

# cat /sys/fs/cgroup/memory/0/memory.limit_in_bytes
4194304

我們能檢查用量:

# cat /sys/fs/cgroup/memory/0/memory.usage_in_bytes
1216512

對這個文件的寫成功並不保證寫入文件的值被變成了設限的設置。這可能因為很多原因,例如湊整到了頁面邊界或者系統記憶體的總量。在寫入之後,要求用戶重讀取這個文件來保證值已經被內核提交:

# echo 1 > memory.limit_in_bytes
# cat memory.limit_in_bytes
4096

memory.failcnt域給出了控制組限制超過的次數。
memory.stat文件給出了統計信息。現在緩存、RSS和激活/不激活頁面數量都能被顯示了。

4. 測試

為了測試功能和實現,可以參看Memcg實現demo。

性能測試也很重要。要查看純記憶體控制器的開銷,tmpfs上的測試將會給你很好看的少量開銷數字。

缺頁(Page-fault)的可測量性也很重要。在平行缺頁測試中,多進程測試比單進程測試要好是因為有共用對象/狀態的噪音。

但是上述兩種是極限測試條件。試著做記憶體控制器下的正常測試總是有好幫助的。

4.1 疑難解答

有時用戶可能發現控制組下的應用程式被OOM killer殺死了,它有幾種原因:

  1. 控制組限制太高。
  2. 用戶正在使用匿名記憶體,而交換記憶體被關閉或者太低了。

通過同步echo 1 > /proc/sys/vm/drop_caches將會幫助除去控制組中的緩存的一些頁面(page cache pages)。

要想知道發生了什麼,禁用OOM_Kill為“10. OOM Control”,再來看看發生的情況。

4.2 任務遷移

當任務從一個控制組遷移到另一個控制組,它的統計預設是不會攜帶走的。從源控制組分配的頁面仍然會保持記賬,當頁面被釋放或者回收後記賬才會摘除。

你可以隨著任務遷移移動記賬。參看第8章“任務遷移時移動記賬”

4.3 移除控制組

控制組可以通過rmdir來移除,但是根據4.1和4.2小節的討論,即使所有任務已經遷移出去了控制組可能仍有一些統計記賬。(因為我們的記賬是針對頁面而不是針對任務)

我們移動統計到父層就不會有記賬變化。

在交換記憶體中的記賬信息在移除控制組時不會被更新。記錄的信息會被丟棄,使用交換記憶體的控制組會作為新的屬主來記賬。

5. 其他介面

5.1 force_empty

memory.force_empty介面可以使控制組的記憶體用量為空:

# echo 0 > memory.force_empty

控制組將被回收,頁面也會被儘可能地回收。

這個介面的典型用例就是在調用rmdie()前。通過rmdir()來使得memcg離線,但是由於文件緩存已經被記賬,memcg仍然可能呆在原地不動。一些超出使用的頁面緩存可能會保持記賬一直到記憶體壓力發生。如果你想要避免這些,force_empty就很有用。

5.2 stat文件

memory.stat文件包含下列統計信息:

記憶體控制組本地狀態

名稱 說明
cache # of bytes of page cache memory.
rss # of bytes of anonymous and swap cache memory (includes transparent hugepages).
rss_huge # of bytes of anonymous transparent hugepages.
mapped_file # of bytes of mapped file (includes tmpfs/shmem)
pgpgin # of charging events to the memory cgroup. The charging event happens each time a page is accounted as either mapped anon page(RSS) or cache page(Page Cache) to the cgroup.
pgpgout # of uncharging events to the memory cgroup. The uncharging event happens each time a page is unaccounted from the cgroup.
swap # of bytes of swap usage
dirty # of bytes that are waiting to get written back to the disk.
writeback # of bytes of file/anon cache that are queued for syncing to disk.
inactive_anon # of bytes of anonymous and swap cache memory on inactive LRU list.
active_anon # of bytes of anonymous and swap cache memory on active LRU list.
inactive_file # of bytes of file-backed memory on inactive LRU list.
active_file # of bytes of file-backed memory on active LRU list.
unevictable # of bytes of memory that cannot be reclaimed (mlocked etc).

分層架構相關的狀態 (memory.use_hierarchy)

hierarchical_memory_limit # of bytes of memory limit with regard to hierarchy under which the memory cgroup is
hierarchical_memsw_limit # of bytes of memory+swap limit with regard to hierarchy under which memory cgroup is.
total_ # hierarchical version of , which in addition to the cgroup’s own value includes the sum of all hierarchical children’s values of , i.e. total_cache

下麵這些額外的統計信息跟CONFIG_DEBUG_VM相關:

recent_rotated_anon VM internal parameter. (see mm/vmscan.c)
recent_rotated_file VM internal parameter. (see mm/vmscan.c)
recent_scanned_anon VM internal parameter. (see mm/vmscan.c)
recent_scanned_file VM internal parameter. (see mm/vmscan.c)

備忘錄:
recent_rotated表示最近的LRU翻轉頻率。recent_scanned表示最近的LRU掃描。顯示出來是為了好調試。

註意:
僅僅匿名和swap緩存記憶體被作為rss統計的一部分被列舉。這個不應該跟resident set size或者控制組使用的物理記憶體弄混淆。
'rss + mapped_file'給出來的才是控制組的resident set size。

註意:
文件和shmem可以跟其它控制組共用。在此情況下,只有當記憶體控制組是頁面緩存的屬主時mapped_file才會被統計。

5.3 swappiness

特定的組可以覆蓋 /proc/sys/vm/swappiness。在根控制組下可調整全局相關的swappiness設置

註意:
不同於全局回收,有限回收強制swappiness為0來防止任何交換,即使有交換記憶體可用也不行。在沒有文件頁面可回收的情況下這可能會導致memcg的OOM-killer。

5.4 failcnt

記憶體控制組提供了memory.failcnt和memory.memsw.failcnt文件。這個failcnt失敗次數顯示了用量計數器觸達限制的次數。當記憶體控制組觸及限制failcnt就會增加,控制組內的記憶體將會被回收。

你可以重置failcnt為0:

# echo 0 > .../memory.failcnt

5.5 usage_in_bytes

記憶體控制組使用一些優化措施來避免不必要的cacheline失敗共用。usage_in_bytes不會顯示記憶體(和交換記憶體)用量的精確值,它是一個模糊值。如果你想要知道更精確的記憶體用量,你應該使用memory.stat中的RSS+CACHE(+SWAP)值。

5.6 numa_stat

它跟numa_maps相似但是基於per-memcg操作。......

每個memcg的numa_stat文件包含“total”,“file”,“anon”和“unevictable”。......

memory.numa_stat的輸出格式:

total=<total pages> N0=<node 0 pages> N1=<node 1 pages> ...
file=<total file pages> N0=<node 0 pages> N1=<node 1 pages> ...
anon=<total anon pages> N0=<node 0 pages> N1=<node 1 pages> ...
unevictable=<total anon pages> N0=<node 0 pages> N1=<node 1 pages> ...
hierarchical_<counter>=<counter pages> N0=<node 0 pages> N1=<node 1 pages> ...

“total”數量是file+anon+unevictable的總和。

6. 分層支持

記憶體控制器支持深度分層和分層級統計。分層是通過在控制組文件系統下創建控制組來生成的。例如有如下一個控制組分層:

    root
  /  |   \
 /   |    \
a    b     c
           | \
           |  \
           d   e

上圖中分層統計也會被使能,所有e的記憶體用量被統計上溯到它的祖宗c和root上。如果有一個父系過量了,回收演算法就會從祖宗和其後代的任務中回收。

6.1 分層統計和回收

分層統計預設是使能的。禁用分層統計已經過時了,試圖這麼做會導致失敗並列印警告到dmesg中。

為了相容,memory.use_hierarchy總是要寫入1:

# echo 1 > memory.use_hierarchy

7. 軟限制Soft limits

軟限制允許更多個記憶體共用。軟限制背後的思想是允許控制組按需使用記憶體,提供如下機制:

  1. 沒有記憶體爭奪。
  2. 不會超過硬限制。

當系統探測到記憶體爭奪或者低記憶體時,控制組就會後推他們的軟限制。如果軟限制非常高,他們就會被儘可能後推來確保控制組不會產生記憶體饑餓。
...

7.1 介面

使用下麵命令來設置軟限制:

# echo 256M > memory.soft_limit_in_bytes

如果想要改為1G:

# echo 1G > memory.soft_limit_in_bytes

註意:
從記憶體控制組內記憶體回收調用開始,軟限制會影響一段很長期的時間。

註意:
建議軟限制總是設置在在硬限制之下,否則硬限制就會獲得優先。

8. 任務遷移時的移動記賬

用戶能隨著任務的遷移對任務進行移動記賬,也就是說,舊控制組內未記賬的任務頁面將會在新控制組內記賬。這個特性在非CONFIG_MMU環境內因為缺乏頁表而不被支持。

8.1 介面

這個特性預設是禁用的。可以寫入到目的控制組memory.move_charge_at_immigrate文件來使能:

# echo (some positive value) > memory.move_charge_at_immigrate

註意:
每個比特位各有意義,應當移動哪種記賬類型。

註意:
只在你移動mm->owner時記賬才會移動。換句話說,就是線程組的領導(原文:a leader of a thread group)。

註意:
如果我們在目的控制組沒有找到足夠空間給任務,我們會試著通過回收記憶體來製造空間。如果不能製造足夠的空間,任務遷移會失敗。

註意:
移動記賬可能耗費幾秒時間。

再次禁用移動記賬:

# echo 0 > memory.move_charge_at_immigrate

8.2 可被移動的記賬類型

每個比特位各有意義,應當移動哪種記賬類型。但是在任何情況下,當記賬到任務當前的(或者舊的)記憶體控制組時,必須說明能被移動的頁面或者swap的數量。

bit 可被移動的記賬類型
0 目標任務用到的匿名頁(或者交換記憶體)。必須使能Swap Extension(2.4小節)
1 目標任務映射的文件頁(正常文件、tmpfs文件<ipc共用記憶體>和tmpfs交換記憶體)。<省略幾句話>,必須使能Swap Extension(2.4小節)

8.3 TODO

所有的移動記賬操作在cgroup_mutex下進行。...

9. 記憶體閾值

記憶體控制組用控制組通知API實現記憶體閾值。允許註冊多個mem和memsw閾值來獲得通知。

要註冊閾值,應用程式必須:

  • 用eventfd(2)來創建eventfd;
  • 打開memory.usage_in_bytes或者memory.memsw.usage_in_bytes;
  • 寫入字元串"<event_fd> "到cgroup.event_control

應用程式在記憶體用量的到達閾值時會被eventfd通知到。它對root和non-root控制組都是適用的。

10. OOM Control

memory.oom_control文件用作OOM通知和其他控制。

記憶體控制組使用控制組通知API實現了OOM通知器。允許註冊多個OOM通知。

要註冊通知器,應用程式必須:

  1. 用eventfd(2)創建eventfd句柄。
  2. 打開memory.oom_control文件。
  3. 寫入字元串“<event_fd> ”到cgroup.event_control。
    當OOM發生時,應用程式將會通過eventfd被通知到。OOM通知對根控制組不生效。

你可以這樣來禁用OOM-killer:

#echo 1 > memory.oom_control

如果OOM-killer被禁用,當控制組下的任務請求可統計的記憶體時,他們將會在記憶體控制組的OOM-waitqueue隊列里掛起/睡眠。

為了運行它,你必須釋放記憶體控制組的OOM狀態,通過:

  • 擴大限制或者減少用量

為了減少用量:

  • 殺死一些任務。
  • 移動一些任務到其他控制組,攜帶著記賬遷移。
  • 移除一些文件(在tmpfs內?)

然後停止的任務會重新工作了。

在讀取時,當前的OOM狀態被顯示:

  • oom_kill_disable:0或者1 (1表示oom-killer禁用)
  • under_oom:0或1 (1表示記憶體控制組在OOM下,任務可以被停止)
  • oom_kill:整數計數器,控制組內被OOM killer殺死的進程數量。

11. 記憶體壓力

壓力等級通知能被用來監測記憶體分配成本。基於壓力,應用程式能實現不同的記憶體資源管理策略。壓力等級定義如下:

  • “low”水平表示系統正在回收記憶體。監控回收活動對於維護緩存水平會有幫助的。在通知中,程式(一般是Activity Manager)可以分析vmstat然後提前採取行動(例如關閉不重要的服務)。
  • “medium”水平表示系統正在經歷中等記憶體壓力。系統可能正在產生交換記憶體,分配文件緩存等等。在這個事件中,應用程式可以進一步分析vmstat/zoneinfo/memcg或者內部記憶體用量統計,釋放那些能很容易重建或者從磁碟中重新讀取的任何資源。
  • “critical”水平表示系統正在遭受衝擊,即將要觸發OOM。應用程式應該盡其所能幫助系統。此時來咨詢vmstat或者其他統計數據可能已經太晚了,建議立刻採取行動。

預設情況下,事件會被層層繁衍上報,直到被處理為止,就是說事件不會穿透。例如,你有三個控制組A->B->C,現在你在控制組A、B、C上都配置了事件監聽器,假設C控制組經受到了壓力,此時只有C會收到通知,而A和B不會收到。這麼做避免了過度廣播消息干擾系統,如果我們在低記憶體或者遭受連續衝擊的情況下會特別有壞處。如果C沒有事件監聽器,那麼B將會收到通知。

有三種可選模式來定義不同的上報行為:

  • “default”: 預設行為。這種模式忽略可選模式參數,留作後向相容。
  • “hierarchy”: 事件總是被上報到根,不管是否有事件監聽器。上述例子中,控制組A、B、C都會收到壓力等級的通知。
  • “local”: 事件被穿透。只有註冊了通知的memcg會收到通知。上述例子中,如果控制組C註冊了“local”通知,那麼C將會收到通知。如果控制組B註冊了“local”通知,然而控制組B絕對不會收到通知,不管C是否註冊了事件監聽器。

壓力等級和事件通知模式是以逗號分隔符字元串定義的,例如“low,hierarchy”定義了分層穿透通知到所有祖先memcg。通知預設是非穿透的。“medium,local”為中等水平定義了穿透通知。

memory.pressure_level文件只是被用來配置eventfd句柄。要註冊同志,應用程式必須:

  1. 用eventfd(2)創建eventfd句柄。
  2. 打開memory.pressure_level。
  3. 寫入字元串“<event_fd> <level[,mode]>”到cgroup.event_control。
    應用程式在記憶體壓力達到或超過指定的等級水平時將會通過eventfd被通知到。對memory.pressure_level的讀寫操作沒有被實現。

測試:
這裡有個小腳本,創建新控制組,配置記憶體限制,配置通知然後在子控制組中經受critical壓力:

# cd /sys/fs/cgroup/memory/
# mkdir foo
# cd foo
# cgroup_event_listener memory.pressure_level low,hierarchy &
# echo 8000000 > memory.limit_in_bytes
# echo 8000000 > memory.memsw.limit_in_bytes
# echo $$ > tasks
# dd if=/dev/zero | read x
(Expect a bunch of notifications, and eventually, the oom-killer will trigger.)

12. TODO

  1. Make per-cgroup scanner reclaim not-shared pages first
  2. 教會控制器來對共用頁面做統計。
  3. 當限制仍舊沒有觸達而用量正在接近時在後臺啟動回收。

總結

總而言之,記憶體控制器是穩定的控制器,已經被提交了,在社區里也得到非常廣泛地討論。

References

  1. Singh, Balbir. RFC: Memory Controller, http://lwn.net/Articles/206697/
  2. Singh, Balbir. Memory Controller (RSS Control), http://lwn.net/Articles/222762/
  3. Emelianov, Pavel. Resource controllers based on process cgroups https://lore.kernel.org/r/[email protected]
  4. Emelianov, Pavel. RSS controller based on process cgroups (v2) https://lore.kernel.org/r/[email protected]
  5. Emelianov, Pavel. RSS controller based on process cgroups (v3) https://lore.kernel.org/r/[email protected]
  6. Menage, Paul. Control Groups v10, http://lwn.net/Articles/236032/
  7. Vaidyanathan, Srinivasan, Control Groups: Pagecache accounting and control subsystem (v3), http://lwn.net/Articles/235534/
  8. Singh, Balbir. RSS controller v2 test results (lmbench), https://lore.kernel.org/r/[email protected]
  9. Singh, Balbir. RSS controller v2 AIM9 results https://lore.kernel.org/r/[email protected]
  10. Singh, Balbir. Memory controller v6 test results, https://lore.kernel.org/r/20070819094658.654.84837.sendpatchset@balbir-laptop
  11. Singh, Balbir. Memory controller introduction (v6), https://lore.kernel.org/r/20070817084228.26003.12568.sendpatchset@balbir-laptop
  12. Corbet, Jonathan, Controlling memory use in cgroups, http://lwn.net/Articles/243795/

.


英文原文:
https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/memory.html
https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/memcg_test.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本項目將使用python3去識別圖片是否為色情圖片,會使用到PIL這個圖像處理庫,並且編寫演算法來劃分圖像的皮膚區域 介紹一下PIL: PIL(Python Image Library)是一種免費的圖像處理工具包,這個軟體包提供了基本的圖像處理功能,如:改變圖像大小,旋轉 圖像,圖像格式轉化,色場空間 ...
  • Android Jetpack Navigation基本使用 本篇主要介紹一下 Android Jetpack 組件 Navigation 導航組件的 基本使用 當看到 Navigation單詞的時候 應該就大概知道 這是一個關於導航用的,下麵我來簡單介紹一下 如何使用Navigation組件的基本 ...
  • APB匯流排信號: APB匯流排狀態機與讀寫Timing IDIE是初始化態; SETUP是從機被PSELx選中以後進入的狀態,只維持一個cycle,下一個周期的上升沿到ENABLE態; ENABLE要使PENABLE HIGH,同時如果沒有繼續transfer那麼從ENABLE跳到IDIE,如果有繼續 ...
  • 本文以C#及VB.NET後端程式代碼示例展示如何將HTML轉為XML文件。轉換時,調用Word API -Free Spire.Doc for .NET 提供的文檔載入方法及文檔保存的方法來實現。轉換的代碼步驟很簡單,具體可參考以下內容。 引入dll 1.通過NuGet安裝dll(2種方法) 1.1 ...
  • 最近在看 C++ 的方法重載,我就在想 C# 中的重載底層是怎麼玩的,很多朋友應該知道 C 是不支持重載的,比如下麵的代碼就會報錯。 #include <stdio.h> int say() { return 1; } int say(int i) { return i; } int main() ...
  • 微信公眾號:趣編程ACE關註可瞭解更多的.NET日常實戰開發技巧,如需源碼 請公眾號後臺留言 源碼;[如果覺得本公眾號對您有幫助,歡迎關註] .Net6下集成微服務網關-Ocelot ​ 視頻講解 網關常見功能 1:路由 routing 2: 請求聚合 3:身份驗證和授權 4:速率限制 5:緩存 6 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 前言 這裡說一下為什麼要在Windows子系統下安裝桌面版Ubuntu,可能有人會問虛擬機不香嗎,雙系統不香嗎?折騰雙系統一不留神就把原來的環境的搞崩了,安裝虛擬機的話可能又會因為電腦硬體限制導致虛擬機非常卡頓無法正常使用,所以這裡是只針對不想經 ...
  • Ubuntu中fcitx安裝失敗問題的原因是ubuntu用的是國外鏡像源,部分軟體下載不了。 對策: 更換國內鏡像源可以使 Ubuntu 的第三方軟體包的下載速度大步提升,這裡以更換為阿裡源為例: 源文件的相關信息在“/etc/apt/”路徑下,使用cd /etc/apt進入文件 源鏈接存放在文件[ ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...