使用mmap来学习/proc/pid/smaps

💡 原文中文,约11000字,阅读约需26分钟。
📝

内容提要

在这篇文章中,作者通过使用/proc/pid/smaps来观测进程的状态,并解释了其中各个字段的含义。作者还通过编写代码来验证了文档中的描述,并讨论了一些特殊情况。最后,作者介绍了ASLR技术对smaps文件分析的影响,并给出了一些示例代码来说明不同情况下的输出结果。

🔎

延伸解读

理解PSS:共享内存的公平分摊

PSS(Proportional Set Size)将共享页按共享进程数均摊,例如两个进程共享60kB,则每个进程的PSS增加30kB。这比RSS更能反映进程实际占用的内存份额。但需注意,PSS的统计粒度是页,若进程间没有共享同一物理页,即使映射同一文件,PSS也可能等于RSS。

写时复制对内存统计的影响

使用MAP_PRIVATE映射文件时,写操作会触发写时复制,生成私有的匿名页。在smaps中表现为Anonymous和Private_Dirty增加,而Shared_Clean减少。这解释了为何私有修改不会影响其他进程,也提醒我们分析内存时需区分共享与私有、干净与脏页。

ASLR对smaps分析的干扰

ASLR随机化进程地址空间,使得每次运行映射地址不同,给对比smaps输出带来困难。作者通过临时禁用ASLR来稳定观察。在实际分析中,若需精确比较,可考虑关闭ASLR,但需注意这会影响系统安全性,仅建议在测试环境中使用。

读取smaps的竞争条件与版本差异

读取/proc/pid/smaps时,内存映射可能正在修改,导致输出不一致,但内核保证部分输出的正确性。此外,不同内核版本和配置下,smaps字段可能变化,如THP相关字段。因此,分析时应结合内核文档,并注意环境差异,避免直接套用。

❓

Q&A

如何临时禁用ASLR以便分析/proc/pid/smaps?

使用命令:sudo sh -c 'echo 0 > /proc/sys/kernel/randomize_va_space' 来临时禁用ASLR。

为什么用mmap分配匿名内存后,smaps中的Rss和Pss没有增加?

因为mmap分配匿名页时,Linux会使用ZeroPage(一个由0填充的只读内存页)来优化填充效率。只有当对该内存进行写操作时,才会触发copy-on-write机制,操作系统才会真正分配物理内存,此时Rss和Pss才会增加。

Pss和Rss有什么区别?

Rss是进程驻留内存大小,即进程使用的物理内存总量;Pss是比例驻留内存大小,它考虑了内存共享:每个共享页面按共享进程数比例计算。例如,一个进程独占1000页,与另一个进程共享1000页,则Pss为1500页。

使用MAP_PRIVATE映射文件并写入后,smaps中哪些字段会变化?

写入会触发copy-on-write,导致Anonymous增加(如4kB),Private_Dirty增加(如4kB),Pss_Dirty增加(如4kB),同时Shared_Clean减少(如从64kB减至60kB),Pss也会相应变化(如从64kB变为34kB)。

Pss_Dirty是如何计算的?

Pss_Dirty是Pss中脏页的部分,只统计共享脏页,每个页面按其共享的进程数比例计算。例如,两个进程共享一个脏页,则每个进程的Pss_Dirty增加该页大小的一半。

读取/proc/pid/smaps时存在竞争条件吗?

是的,读取/proc/pid/maps或/proc/pid/smaps时存在竞争条件。在内存映射修改的同时进行部分读取可能会遇到不一致性,但Linux内核保证了部分输出的正确性。

➡️

继续阅读