Memory Resource Controller 記憶體資源控制器 註意: 這個文檔完完全全地過時了,需要整個地重寫。但它還是包含了有用的信息,所以我們仍舊把它保留在這裡,但是如果你需要深入理解的話,需要確保核對過當前的代碼。 註意: 記憶體資源控制器在本文檔中指的是記憶體控制器。不要混淆了這裡記憶體控 ...
Memory Resource Controller
記憶體資源控制器
目錄
- Memory Resource Controller
- 記憶體資源控制器
- 記憶體控制器的優點和目的
- 1. 歷史
- 2. 記憶體控制
- 3. 用戶介面
- 4. 測試
- 5. 其他介面
- 6. 分層支持
- 7. 軟限制Soft limits
- 8. 任務遷移時的移動記賬
- 9. 記憶體閾值
- 10. OOM Control
- 11. 記憶體壓力
- 12. TODO
- 總結
- References
註意:
這個文檔完完全全地過時了,需要整個地重寫。但它還是包含了有用的信息,所以我們仍舊把它保留在這裡,但是如果你需要深入理解的話,需要確保核對過當前的代碼。
註意:
記憶體資源控制器在本文檔中指的是記憶體控制器。不要混淆了這裡記憶體控制器和硬體中使用的記憶體控制器。
(給編輯者)在本文檔中:
當我們提到記憶體控制器的控制組(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來處理,任務能通過小時/天/月或者年的周期來擴展它的處理能力,但是記憶體只能被覆用來完成任務。
記憶體控制器的實現已經被劃分成多個階段,他們是:
- Memory controller
- mlock(2) controller
- Kernel user memory accounting and slab control
- 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顯示了控制器的幾個重要方面:
- 以控制組(per cgroup)為單位進行統計
- 每個mm_struct知道它屬於哪個cgroup
- 每個頁面有一個指針指向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殺死了,它有幾種原因:
- 控制組限制太高。
- 用戶正在使用匿名記憶體,而交換記憶體被關閉或者太低了。
通過同步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 |
下麵這些額外的統計信息跟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
軟限制允許更多個記憶體共用。軟限制背後的思想是允許控制組按需使用記憶體,提供如下機制:
- 沒有記憶體爭奪。
- 不會超過硬限制。
當系統探測到記憶體爭奪或者低記憶體時,控制組就會後推他們的軟限制。如果軟限制非常高,他們就會被儘可能後推來確保控制組不會產生記憶體饑餓。
...
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通知。
要註冊通知器,應用程式必須:
- 用eventfd(2)創建eventfd句柄。
- 打開memory.oom_control文件。
- 寫入字元串“<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句柄。要註冊同志,應用程式必須:
- 用eventfd(2)創建eventfd句柄。
- 打開memory.pressure_level。
- 寫入字元串“<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
- Make per-cgroup scanner reclaim not-shared pages first
- 教會控制器來對共用頁面做統計。
- 當限制仍舊沒有觸達而用量正在接近時在後臺啟動回收。
總結
總而言之,記憶體控制器是穩定的控制器,已經被提交了,在社區里也得到非常廣泛地討論。
References
- Singh, Balbir. RFC: Memory Controller, http://lwn.net/Articles/206697/
- Singh, Balbir. Memory Controller (RSS Control), http://lwn.net/Articles/222762/
- Emelianov, Pavel. Resource controllers based on process cgroups https://lore.kernel.org/r/[email protected]
- Emelianov, Pavel. RSS controller based on process cgroups (v2) https://lore.kernel.org/r/[email protected]
- Emelianov, Pavel. RSS controller based on process cgroups (v3) https://lore.kernel.org/r/[email protected]
- Menage, Paul. Control Groups v10, http://lwn.net/Articles/236032/
- Vaidyanathan, Srinivasan, Control Groups: Pagecache accounting and control subsystem (v3), http://lwn.net/Articles/235534/
- Singh, Balbir. RSS controller v2 test results (lmbench), https://lore.kernel.org/r/[email protected]
- Singh, Balbir. RSS controller v2 AIM9 results https://lore.kernel.org/r/[email protected]
- Singh, Balbir. Memory controller v6 test results, https://lore.kernel.org/r/20070819094658.654.84837.sendpatchset@balbir-laptop
- Singh, Balbir. Memory controller introduction (v6), https://lore.kernel.org/r/20070817084228.26003.12568.sendpatchset@balbir-laptop
- 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