I think statm
是一个近似的简化smaps
,获得成本更高。我看了源码后得出这个结论:
smaps
您看到的信息smaps
定义于/fs/proc/task_mmu.c https://github.com/torvalds/linux/blob/master/fs/proc/task_mmu.c#L631:
static int show_smap(struct seq_file *m, void *v, int is_pid)
{
(...)
struct mm_walk smaps_walk = {
.pmd_entry = smaps_pte_range,
.mm = vma->vm_mm,
.private = &mss,
};
memset(&mss, 0, sizeof mss);
walk_page_vma(vma, &smaps_walk);
show_map_vma(m, vma, is_pid);
seq_printf(m,
(...)
"Rss: %8lu kB\n"
(...)
mss.resident >> 10,
中的信息mss
被使用walk_page_vma
定义于/mm/pagewalk.c https://github.com/torvalds/linux/blob/master/mm/pagewalk.c#L288。但是,那mss
member resident
未填写walk_page_vma
- 反而,walk_page_vma
调用指定的回调smaps_walk
:
.pmd_entry = smaps_pte_range,
.private = &mss,
像这样:
if (walk->pmd_entry)
err = walk->pmd_entry(pmd, addr, next, walk);
那么我们的回调是做什么的,smaps_pte_range
in /fs/proc/task_mmu.c https://github.com/torvalds/linux/blob/master/fs/proc/task_mmu.c, 做?
它调用smaps_pte_entry
and smaps_pmd_entry
在某些情况下,两者都调用statm_account()
,这反过来...升级resident
尺寸!所有这些函数都在已经链接的中定义task_mmu.c
所以我没有发布相关的代码片段,因为它们可以在链接的源中轻松看到。
PTE 代表页表条目,PMD 代表页面中间目录。因此,基本上我们会迭代与给定进程关联的页面条目,并根据情况更新 RAM 使用情况。
statm
您看到的信息statm
定义于/fs/proc/array.c https://github.com/torvalds/linux/blob/master/fs/proc/array.c:
int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
struct pid *pid, struct task_struct *task)
{
unsigned long size = 0, resident = 0, shared = 0, text = 0, data = 0;
struct mm_struct *mm = get_task_mm(task);
if (mm) {
size = task_statm(mm, &shared, &text, &data, &resident);
mmput(mm);
}
seq_put_decimal_ull(m, 0, size);
seq_put_decimal_ull(m, ' ', resident);
seq_put_decimal_ull(m, ' ', shared);
seq_put_decimal_ull(m, ' ', text);
seq_put_decimal_ull(m, ' ', 0);
seq_put_decimal_ull(m, ' ', data);
seq_put_decimal_ull(m, ' ', 0);
seq_putc(m, '\n');
return 0;
}
这次,resident
被填充为task_statm
。这个有两种实现,一种是/fs/proc/task_mmu.c https://github.com/torvalds/linux/blob/master/fs/proc/task_mmu.c和第二名/fs/proc/task_nomm.c https://github.com/torvalds/linux/blob/master/fs/proc/task_nommu.c。由于它们几乎肯定是相互排斥的,因此我将重点关注task_mmu.c
(其中还包含task_smaps
)。在这个实现中我们看到
unsigned long task_statm(struct mm_struct *mm,
unsigned long *shared, unsigned long *text,
unsigned long *data, unsigned long *resident)
{
*shared = get_mm_counter(mm, MM_FILEPAGES);
(...)
*resident = *shared + get_mm_counter(mm, MM_ANONPAGES);
return mm->total_vm;
}
它查询一些计数器,即MM_FILEPAGES
and MM_ANONPAGES
。这些计数器在内存的不同操作期间被修改,例如do_wp_page
定义于/mm/memory.c https://github.com/torvalds/linux/blob/master/mm/memory.c。所有修改似乎都是由位于的文件完成的/mm/
而且好像还不少,所以这里就不一一列举了。
结论
smaps
对所有引用的内存区域进行复杂的迭代并进行更新resident
使用收集到的信息的大小。statm
使用其他人已经计算出的数据。
最重要的部分是,虽然smaps
每次以独立的方式收集数据,statm
使用在进程生命周期期间递增或递减的计数器。有很多地方需要做记账,也许有些地方没有像他们应该的那样升级柜台。这就是为什么国际海事组织statm
劣于smaps
,即使需要更少的 CPU 周期来完成。
请注意,这是我根据常识得出的结论,但我可能是错的 - 也许计数器递减和递增中没有内部不一致,相反,它们对某些页面的计数可能与smaps
。在这一点上,我相信将其交给一些经验丰富的内核维护人员是明智的。