Linux操作系统:/ proc/[pid]/smaps vs/proc/[pid]/statm

tum*_*ler 6 linux memory-management

我想计算单个进程的内存使用情况.经过一些研究后,我遇到了smaps和statm.

首先是什么是smaps和statm?有什么不同?

statm有一个字段RSS和smaps我总结了所有的RSS值.但是这些值对于同一过程是不同的.我知道statm措施在页面中.为了进行比较,我将该值转换为kb,如在smaps中.但这些价值观并不平等.为什么这两个值不同,即使它们代表同一过程的rss值?

statm
232214 80703 7168 27 0 161967 0 (measured in pages, pages size is 4096)

smaps
Rss 1956
Run Code Online (Sandbox Code Playgroud)

我的目标是计算单个进程的内存使用情况.我对两个价值观感兴趣.USS和PSS.我可以通过使用smaps来获得这两个值吗?这个价值是否正确?另外,我想以百分比形式返回该值.

rr-*_*rr- 8

我认为statm是一个近似的简化smaps,这是更昂贵的.在我查看来源后,我得出了这个结论:

smaps

您在其中看到的信息smaps定义/fs/proc/task_mmu.c如下:

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,
Run Code Online (Sandbox Code Playgroud)

信息输入msswalk_page_vma定义中使用/mm/pagewalk.c.但是,该mss成员resident未填写walk_page_vma- 而是walk_page_vma调用在smaps_walk以下指定的回调:

.pmd_entry = smaps_pte_range,
.private = &mss,
Run Code Online (Sandbox Code Playgroud)

像这样:

  if (walk->pmd_entry)
      err = walk->pmd_entry(pmd, addr, next, walk);
Run Code Online (Sandbox Code Playgroud)

那么我们的回调smaps_pte_range/fs/proc/task_mmu.c什么呢?它呼叫smaps_pte_entry,smaps_pmd_entry在某些情况下,两者都呼叫statm_account(),反过来......升级resident大小!所有这些功能都在已经链接的中定义,task_mmu.c所以我没有发布相关的代码片段,因为它们可以在链接的源中轻松看到.

PTE代表页表输入,PMD是页面中间目录.因此,基本上我们遍历与给定进程关联的页面条目,并根据具体情况更新RAM使用情况.

statm

您在其中看到的信息statm/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;
}
Run Code Online (Sandbox Code Playgroud)

这一次,resident充满了task_statm.这个有两个实现,一个在/fs/proc/task_mmu.c第二个和第二个/fs/proc/task_nomm.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;
}
Run Code Online (Sandbox Code Playgroud)

它查询一些计数器,即MM_FILEPAGESMM_ANONPAGES.这些计数器在内存的不同操作期间被修改,例如do_wp_page定义的/mm/memory.c.所有的修改似乎都是由位于的文件完成的,/mm/而且似乎有很多,所以我没有在这里包含它们.

结论

smaps通过所有引用的内存区域进行复杂的迭代,并resident使用收集的信息更新大小.statm使用已由其他人计算过的数据.

最重要的部分是,smaps每次以独立方式收集数据时,statm使用在过程生命周期中递增或递减的计数器.有很多地方需要进行簿记,也许有些地方不会像他们应该那样升级柜台.这就是为什么IMO statm不如smaps,即使它需要较少的CPU周期才能完成.

请注意,这是我根据常识得出的结论,但我可能错了 - 也许在计数器递减和递增中没有内部不一致,相反,它们可能会以不同的方式计算某些页面smaps.在这一点上,我认为将它带给一些经验丰富的内核维护者是明智之举.