Notes
main
main
  • Introduction
  • linuxKernel
    • tips
    • make_help
    • old linux
      • compile_linux0.11
      • TestEnvironment
      • load_setup
      • get_hard_data
    • list
    • plist
    • fifo
    • idr
    • xarray
    • rbtree
    • maple_tree
    • syscall
    • bitmap
    • page
    • page_flags
    • page_size
    • page mapcount
    • page refcount
    • folio
    • slub
      • proc_slabinfo
      • slub_theory
      • kmalloc_kfree
      • kmem_cache
      • slab_alloc
      • slab_free
      • proc_meminfo_SReclaimable_SReclaimable
    • vmalloc
    • brk
    • mmap
    • mremap
    • mprotect
    • madvise
    • read
    • write
    • shmem
    • huge_page
    • page_fault
    • rmap
    • lru
    • multi-gen-LRU
    • page_reclaim
    • page_cache
    • page_table
    • rcu
    • kvm
    • aarch64_boot
    • tracing_system
    • cache_coherence_and_memory_consistency
    • cpu_speculates
    • mmap_lock
    • per-vma_lock
    • cgroup
    • symbol
    • campact
    • page_ext
    • mempool
    • kernelstack
    • filesystem
    • io_stack
    • workingset
    • ioremap
    • sched_period
  • linuxDebug
    • openocd_openjtag
    • i2c_tools
    • objdump
    • addr2line
    • gdb_useage
    • debug_linux_kernel_via_gdb
    • debug_linux_module_via_gdb
    • early_boot
    • sequentially_execute
    • dynamic_debug
    • research_linuxKernel_by_patch
    • tracefs
    • ebpf
    • bpftrace
    • perf
    • flame_graph
    • crash
    • ASAN_HWASAN_MTE_check_mem_bug
    • page_owner
    • vmtouch
    • fio
    • benchmark
  • linuxSystem
    • common
      • system_version
      • procfs
      • proc_sys_vm
      • cmd_ps
      • makefile
      • file_descriptor
      • psi
      • ulimit
      • top
      • delay_accounting
    • ubuntu
      • custom_kernel
      • get_cmd_src
      • record_ssh_info
      • log
      • run_custom_script
      • repo
      • cockpit
      • nfs
      • tftp
      • misc
    • fedora
      • system_upgrade
      • custom_kernel
      • lvextend
      • yt-dlp
      • jellyfin
  • linuxDriver
    • i2c_peripherals_driver
    • spi_peripherals_driver
    • gpio_subsystem
    • IRQ_driver
    • blockIO_unblockIO_async
    • linux_own_driver
    • misc_device
    • input_device
    • timer
    • atomic_spinlock_semaphore_mutex
    • lcd
    • touch_screen
    • debugfs
    • v4l2
    • mmap
  • hardware
    • paging_mmu_pt
    • iommu
  • process_thread_scheduler
    • scheduler01
    • scheduler02
    • scheduler03
    • scheduler04
    • scheduler05
    • scheduler06
  • memory_management
    • mm1
    • mm2
    • mm3
    • mm4
    • mm5
  • input_output_filesystem
    • io_fs_01
    • io_fs_02
    • io_fs_03
    • io_fs_04
  • lock_and_lockup_detector
    • general_lock
    • hung_task
    • softLockup_hardLockup
    • crash_experiment
  • MIT_6.S081
    • 6.S081_Operating_System_Engineering
    • Schedule.md
    • Class
      • Overview
      • Administrivia
    • Labs
      • Tools
      • Guidance
      • startup
      • syscall
      • page_table
      • Calling_Convention
      • traps
    • xv6
      • xv6
    • References.md
  • qemu
    • qemu_buildroot
    • qemu_busybox.md
    • Serial.md
    • demo_mini2440
      • 0_compilation_error_summary
      • 1_compilation_steps
      • 2_operation_mode
      • 3_transplant_tools_libraries
      • 4_tools_use
      • reference_website
  • tools
    • getKernelSourceCodeList
    • nat
    • shell
    • translating
    • YouCompleteMe
    • cscope
    • global
    • vscode
    • vim
    • binary
    • markdown
    • draw
    • git
    • tig
    • tmux
    • mail_client
    • download_patchset_from_LKML
    • minicom
    • clash
  • other
    • interview
    • interview_c_base
    • know_dontknow
    • Stop-Ask-Questions-The-Stupid-Ways
    • How-To-Ask-Questions-The-Smart-Way
    • docker
    • buildroot
    • rv32_to_rv64
Powered by GitBook
On this page
  • 简介
  • 编译
  • 查看 cgroup 版本
  • 对比不同版本的 cgroup
  • 接口描述
  • 如何限制 cgroup memory 的内存使用?

Was this helpful?

  1. linuxKernel

cgroup

简介

cgroup 能够限制进程的 CPU,memory,IO 等资源的使用比例。

编译

编译 Linux 内核时,使能 CONFIG_MEMCG 选项,打开 cgroup memory 功能。

查看 cgroup 版本

$ stat -fc %T /sys/fs/cgroup/
cgroup2fs
## or
$ mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)

对比不同版本的 cgroup

cgroup v1
cgroup v2
备注

memory.usage_in_bytes

memory.current

当前内存使用量

memory.max_usage_in_bytes

memory.peak

最大内存使用量

memory.soft_limit_in_bytes

memory.high

当此 cgroup 内存超过此值,进行内存回收

memory.limit_in_bytes

memory.max

设置最大能够使用的内存大小

memory.force_empty

memory.reclaim

回收内存,v1 全部回收,v2 回收指定内存量

memory.swappiness

memory.reclaim

设置回收 anon:file page 的趋势

memory.memsw.usage_in_bytes

memory.swap.current

当前 swap 使用量

memory.memsw.max_usage_in_bytes

memory.swap.peak

最大 swap 使用量

memory.memsw.limit_in_bytes

memory.swap.max

设置最大能够使用的 swap 使用量

memory.memsw.failcnt

memory.swap.events

swap 失败次数

下面主要介绍 cgroup v2 的源码以及原理。

接口描述

memory.current

read-only

当前 cgroup 及其所有子组正在使用的内存大小

memory.peak

read-only

当前 cgroup 及其所有子组的最大内存使用量。

memory.min

read-write, the default is 0.

如果 cgroup 内存使用量低于 memory.min 时,肯定不会回收 cgroup 内存。

如果 cgroup 内存使用量比 memory.min 多,将超出部分按照比例进行回收,从而减少
回收压力。

memory.low

read-write, the default is 0.

如果 cgroup 内存使用量低于 memory.low 时,尽可能不回收 cgroup 内存。除非没有
可回收内存可用,否则不会回收 cgroup 内存。

如果 cgroup 内存使用量比 memory.low 多,将超出部分按照比例进行回收,从而减少
回收压力。

memory.high

read-write, default is max.

如果 cgroup 内存使用量比 memory.high 多,cgroup 包含的进程被节流,并且强制
进行内存回收,但是肯定不会触发 OOM killer。

memory.max

read-write, default is max.

如果 cgroup 内存使用量达到 memory.max 并且无法减少,那么在 cgroup 中会调用
OOM killer。在某些情况下,内存使用量可能会暂时超过限制。

memory.reclaim

write-only

用于触发 cgroup 中的内存回收,这属于主动回收,并不意味着 cgroup 上有内存压力。

需要指定要回收的字节数,如果回收的内存量少于指定的数量,则返回 -EAGAIN。
如:echo "1G" > memory.reclaim

配置回收行为,指定从 anon/file 进行回收,与系统 vm.swappiness 同含义。
如:echo "2M swappiness=0" > /sys/fs/cgroup/memory.reclaim

memory.oom.group

read-write, default value is 0

如果 memory.oom.group 设置为 1,当 cgroup 触发 OOM killer 时,属于此 cgroup
的所有 task 都会被杀掉(包括子组里面所有的 task)。这可用于避免只杀掉一个 task,
导致其他 task 也无法正常工作的情况,保证工作负载的完整性。

如果 memory.oom.group 设置为 0,那么 OOM killer 只杀掉内存负载最重的一个 task。

oom_score_adj 设置为 -1000 的 task,永远不会被 OOM killer 杀掉。

memory.events

read-only,包含所有子组的 events。文件中定义了以下条目。

low             cgroup 内存使用量低于low,但是由于内存压力较高而被回收的次数。
high            cgroup 内存使用量超过 high,执行直接内存回收的次数。
max             cgroup 内存使用量超过 max 的次数。
oom             cgroup内存使用量达到限制,并且分配内存即将失败的次数。
oom_kill        被 OOM killer 杀掉的进程数目
oom_group_kill  被 OOM killer 杀掉的 group 数目

memory.events.local

每一个字段含义与 memory.events 相同,但是 memory.events.local 只包含当前
cgroup 的 events。

memory.stat

read-only,这是 cgroup 不同类型的内存使用量信息,以下统计单位是 bytes。

如果一个 entry 不属于 per-node counter,那么它将不会显示在 memory.numa_stat 中。
并且使用 npn (non-per-node) 来标记这些 entry。

文件中定义了以下 entry :

anon                匿名页的内存使用量,包括 brk(), sbrk() and mmap(MAP_ANONYMOUS)
file                文件页的内存使用量,包括 tmpfs and shared memory

kernel (npn)        内核空间的总内存使用量,
                    包括 kernel_stack, pagetables, percpu, vmalloc, slab
kernel_stack        kernel stacks 的内存使用量
pagetables          page tables 的内存使用量
sec_pagetables      secondary page tables 的内存使用量,
                    包括 x86/arm64 上的 KVM mmu page tables,IOMMU page tables
percpu (npn)        per-cpu kernel data structures 的内存使用量
sock (npn)          network transmission buffers 的内存使用量
vmalloc (npn)       vmap backed memory 的内存使用量
slab_reclaimable    能够被回收的 slab 内存使用量,包含 dentries and inodes.
slab_unreclaimable  不能被回收的 slab 内存使用量,slab 分配器默认行为。
slab (npn)          slab 分配器分配给内核空间使用的总内存使用量,
                    包含 slab_reclaimable + slab_unreclaimable

shmem               swap-backed shared memory 的内存使用量,
                    包含 tmpfs, shm segments, shared anonymous mmap()s

zswap               zswap compression backend 的内存使用量
zswapped            交换到 zswap 的应用程序内存大小

file_mapped         通过 mmap() 映射的文件页的内存使用量
file_dirty          脏文件页的内存使用量
file_writeback      正在回写到磁盘的脏文件页的内存使用量

swapcached          swapcache 的内存使用量

inactive_anon       在不同 LRU list 上的内存使用量
active_anon
inactive_file
active_file
unevictable

pgscan (npn)              内存回收时,在 inactive LRU list 上扫描的总页数
pgsteal (npn)             内存回收时,成功回收的总页数
pgscan_kswapd (npn)       内存回收时,通过 kswapd 在 inactive LRU list 上扫描的页数
pgscan_direct (npn)       内存回收时,通过 direct reclaim 在 inactive LRU list 上扫描的页数
pgscan_khugepaged (npn)   内存回收时,通过 khugepaged 在 inactive LRU list 上扫描的页数
pgsteal_kswapd (npn)      内存回收时,通过 kswapd 成功回收的页数
pgsteal_direct (npn)      内存回收时,通过 direct reclaim 成功回收的页数
pgsteal_khugepaged (npn)  内存回收时,通过 khugepaged 成功回收的页数

pgrefill (npn)            在 active LRU list 上扫描的总页数
pgactivate (npn)          移动到 active LRU list 的总页数
pgdeactivate (npn)        移动到 inactive LRU list 的总页数
pglazyfree (npn)          在内存压力下,lazyfree 的页数
pglazyfreed (npn)         回收 lazyfree 的页数

workingset_refault_anon   之前回收的匿名页,再一次触发 pagefault 的次数
workingset_refault_file   之前回收的文件页,再一次触发 pagefault 的次数
workingset_activate_anon  之前回收的匿名页,马上立刻再一次触发 pagefault 的次数。
workingset_activate_file  之前回收的文件页,马上立刻再一次触发 pagefault 的次数。
workingset_restore_anon   之前回收的匿名页(位于 active workingset),
                          马上立刻再一次触发 pagefault 的次数
workingset_restore_file   之前回收的文件页(位于 active workingset),
                          马上立刻再一次触发 pagefault 的次数
workingset_nodereclaim    shadow node 被回收的次数

pgfault (npn)             发生 page fault 的次数,包括 minorfault + majorfault。
pgmajfault (npn)          发生 major page fault 的次数

zswpin                    从 zswap 移入到内存的页数
zswpout                   从内存移出到 zswap 的页数
zswpwb                    从 zswap 回写到 swap 的页数

anon_thp                  THP 匿名页的内存使用量
file_thp                  THP 文件页的内存使用量
shmem_thp                 THP shm, tmpfs, shared anonymous mmap()s 的内存使用量
thp_fault_alloc (npn)     在 page fault 过程中分配 THP 的页数
thp_collapse_alloc (npn)  合并现有 page 范围而分配 THP 的页数
thp_swpout (npn)          直接 swapout 整个 THP 的页数
thp_swpout_fallback (npn) 由于无法分配连续的 swap 空间,进行拆分 THP 后再 swapout 的页数

memory.numa_stat

read-only,这是 cgroup 每个 node 的不同类型的内存使用量信息,以下统计单位是 bytes。

如果 page 允许从任何 node 分配,这个节点提供 memcg 中 NUMA 本地性信息的可见性。
如:通过将此信息与进程的 CPU 分配相结合,来评估应用程序的性能。

输出格式如下:

type N0=<bytes in node 0> N1=<bytes in node 1> ...

详细解析参考 memory.stat

memory.swap.current

read-only

当前 cgroup 及其所有子组正在使用的 swap 使用量

memory.swap.peak

read-only

当前 cgroup 及其所有子组的最大 swap 使用量。

memory.swap.high

read-write,default is max.

当 cgroup 的 swap 使用量超过这个限制时,所有内存分配都会被限制,并且调用
用户空间自定义 OOM 处理程序。

一旦达到这个限制,cgroup 就不能正常运行。这个参数不是用来管理 swap 使用量。

memory.swap.max 设置 swap 最大使用量,如果其他内存能够被回收,cgroup 继续运行。

memory.swap.max

read-write,default is max.

设置 cgroup 最大能够使用的 swap 使用量。如果 cgroup 的 swap 使用量达到
这个限制,cgroup 的匿名内存将不会被 swapout。

memory.swap.events

read-only,文件中定义了以下条目。

high    cgroup 的 swap 使用量超过 memory.swap.high 的次数
max     cgroup 的 swap 使用量即将超过 memory.swap.max 并且 swap 分配失败的次数。
fail    系统 swap 使用量用完或达到 memory.swap.max 限制,导致 swap 分配失败的次数。

memory.zswap.current

read-only

zswap compression backend 的内存使用量

memory.zswap.max

read-write,default is max.

设置 cgroup 最大能够使用的 zswap 使用量。如果 cgroup 的 zswap pool 达到
这个限制,它将拒绝任何存储,直至存在 entry 回写到磁盘。

memory.zswap.writeback

read-write,default is "1".

root cgroup 的初始化值等于 1,当新 cgroup 被创建时,将继承父 cgroup 的当前值。

当此值设置为 0 时,将禁用 zswap 回写和由于 zswap 存储失败而导致的交换。
如果 zswap 重复存储失败(如:page 是不可压缩的),用户可以观察到回收效率低
(因为相同 page 可能会一次又一次地被拒绝)。

注意,这与 memory.swap.max = 0 略有不同,因为 zswap 仍然允许将 page 写入
zswap pool。

memory.pressure

read-only

显示内存压力 PSI 信息。

如何限制 cgroup memory 的内存使用?

当触发 pagefault 申请物理内存时,调用 mem_cgroup_charge() 判断当前进程所属 cgroup 是否有足够的空闲内存。如果有,申请成功。否则,申请失败 OOM。

mem_cgroup_charge() -> charge_memcg() -> try_charge() -> try_charge_memcg()
    consume_stock()

    page_counter_try_charge()

    refill_stock()
    mem_cgroup_handle_over_high()

    if PF_MEMALLOC, page_counter_charge()

    try_to_free_mem_cgroup_pages()
    drain_all_stock()
    mem_cgroup_oom()

先调用 consume_stock() 从 memcg_stock 中查找是否有足够的空闲内存,如果有, 申请成功,直接返回。否则,

调用 page_counter_try_charge() 从 memcg->memory 中查找是否有至少 64 页的 空闲内存?

如果有,申请成功。然后调用 refill_stock() 将多申请的内存存储到 memcg_stock 中, 同时如果 cgroup 内存使用量超过 memory.high,调用 mem_cgroup_handle_over_high() 进行内存回收等处理。

如果没有,申请失败,但是有 PF_MEMALLOC 标志,直接调用 page_counter_charge() 无条件申请内存成功,直接返回。

如果没有,申请失败,但是允许阻塞,调用 try_to_free_mem_cgroup_pages() 进行 直接内存回收,如果回收后有足够的空闲内存,再从头开始申请内存。否则,

调用 drain_all_stock() 将 memcg_stock 缓存的所有内存释放到 cgroup 中,并且 再从头开始申请内存。

实在没有办法了,目前 cgroup 中使用的内存量已经达到 memory.max,调用 mem_cgroup_oom() 触发 OOM killer,并且再从头开始申请内存。

Previousper-vma_lockNextsymbol

Last updated 9 months ago

Was this helpful?