RSS
总体来说,RSS 机制是一个常用的,如果是本来就理解了 mmap 接口, 容易理解的机制
接口
内核中提供了好几个接口,其实一共就三个元素,当然还有很多其他的方法来间接获取,例如
- oom 报错
- smaps 中累加各个 vma 区域的
do_task_stat : /proc/self/stat
static inline unsigned long get_mm_rss(struct mm_struct *mm)
{
return get_mm_counter(mm, MM_FILEPAGES) +
get_mm_counter(mm, MM_ANONPAGES) +
get_mm_counter(mm, MM_SHMEMPAGES);
}
proc_pid_statm -> task_statm : /proc/self/statm
unsigned long task_statm(struct mm_struct *mm,
unsigned long *shared, unsigned long *text,
unsigned long *data, unsigned long *resident)
{
*shared = get_mm_counter_sum(mm, MM_FILEPAGES) +
get_mm_counter_sum(mm, MM_SHMEMPAGES);
*text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
>> PAGE_SHIFT;
*data = mm->data_vm + mm->stack_vm;
*resident = *shared + get_mm_counter_sum(mm, MM_ANONPAGES);
return mm->total_vm;
}
# /proc/PID/statm - 简洁格式(单位:页)
$ cat /proc/self/statm
665 443 88 0 0 0 0
| | |
| | +--- shared (包含 file+shmem)
| +------ resident (RSS)
+---------- size (VSZ)
RSS vs VSZ 的区别
指标 含义 包含 不包含 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ VSZ (Virtual Memory Size) 虚拟地址空间总大小 所有已映射区域(不论是否分配物理页) - RSS (Resident Set Size) 实际驻留物理内存的页面 已分配物理内存的文件页+匿名页+共享页 已换出页面、未访问的零页
proc_pid_status -> task_mem : /proc/PID/status
# /proc/PID/status
$ grep -E 'VmRSS|Rss' /proc/self/status
VmRSS: 1772 kB # RSS 总量
RssAnon: 108 kB # 匿名页部分
RssFile: 1652 kB # 文件映射页部分
RssShmem: 12 kB # 共享内存页部分
RSS 的三类页面详解
计数器 说明 典型场景 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ MM_ANONPAGES 匿名页 malloc 分配的堆内存、栈、私有匿名映射 MM_FILEPAGES 文件映射页 mmap 映射的文件、可执行文件的代码段 MM_SHMEMPAGES 共享内存页 共享匿名映射(MAP_SHARED|MAP_ANON)、tmpfs
首先回顾一下 mmap-flags.c 中
问题 1 : mmap 到底统计到那里?
配合 rss.c 中的结果,我认为是很清晰了
首先 tmpfs 和 memfd 没有区别,只是可见性的问题:
╔════════════════════════════════════════════════════════════╗
║ SUMMARY ║
╠════════════════════════════════════════════════════════════╣
║ File Type │ Map Type │ After Read │ After Write ║
╠═══════════════╪═════════════╪══════════════╪══════════════╣
║ Regular File │ PRIVATE │ RssFile │ RssAnon ║
║ Regular File │ SHARED │ RssFile │ RssFile ║
║ tmpfs File │ PRIVATE │ RssShmem │ RssAnon ║
║ tmpfs File │ SHARED │ RssShmem │ RssShmem ║
║ Anonymous │ SHARED │ N/A │ RssShmem ║
╚═══════════════╧═════════════╧══════════════╧══════════════╝
我认为其中的关键区别在于,在进行 swap out 的时候,sync to fs 还是需要真的写入到 swap 中去。所以,只有 shared mmap ext4 才算是 MM_FILEPAGES 。 然后分析其他的类型,如果是共享的,那么就是 RssShmem ,如果不可以的,也就是 PRIVATE map ,然后 cow 的, 那么就统计到 RssAnon 中去
问题 2 :
如果是 open 一个 tmpfs ,然后 write 统计结果是什么样子的?
不会,因为 RSS 统计的是虚拟地址空间的
何时增减 RSS
增加 RSS 的场景:
// mm/memory.c - 处理 pf inc_mm_counter(vma->vm_mm, mm_counter_file(folio)); // 文件页 inc_mm_counter(vma->vm_mm, MM_ANONPAGES); // 匿名页
减少 RSS 的场景(页面释放/换出时):
// mm/rmap.c - 页面回收 dec_mm_counter(mm, mm_counter(folio)); // 释放页面
// 页面换出:从 ANONPAGES 移到 SWAPENTS dec_mm_counter(mm, MM_ANONPAGES); inc_mm_counter(mm, MM_SWAPENTS); // 注意:这不计入 RSS
可以看到,这都是 page table 的构建和拆除的时候进行统计的。
有趣的问题
memfd + vfio 直通,虚拟机大小为 12G ,结果发现虚拟机的 RSS 是 7193MiB ,一般来说 由于共享机制,一个 process 的 RSS 显示的内存使用比实际要大,但是这是一个例外,因为 vfio 机制自动的 pin 了所有的内存, 但是 qemu 没有去 touch 。
VM PID CAP MiB CURRENT MiB RSS MiB AVAIL MiB RSV MiB STATE
yyds-fs 291205 12288 12288 7193 8294 2457 observe-only: VFIO passthrough
本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。