子系统跟踪点:kmem

kmem 跟踪系统捕获与内核中对象和页面分配相关的事件。 广义上讲,主要有五个小标题。

  • 未知类型的小对象的 Slab 分配 (kmalloc)

  • 已知类型的小对象的 Slab 分配

  • 页面分配

  • 每个 CPU 的分配器活动

  • 外部碎片

本文档描述了每个跟踪点是什么以及为什么它们可能有用。

1. 未知类型的小对象的 Slab 分配

kmalloc               call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s
kmalloc_node  call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s node=%d
kfree         call_site=%lx ptr=%p

这些事件的频繁活动可能表明特定的缓存是合理的,特别是如果 kmalloc slab 页面由于分配模式而导致严重的内部碎片。 通过将 kmalloc 与 kfree 相关联,可以识别内存泄漏以及分配位置。

2. 已知类型的小对象的 Slab 分配

kmem_cache_alloc      call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s
kmem_cache_alloc_node call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s node=%d
kmem_cache_free               call_site=%lx ptr=%p

这些事件的用法与 kmalloc 相关事件类似,只是将事件锁定到特定缓存可能更容易。 在撰写本文时,没有关于从哪个 slab 分配的信息,但是 call_site 通常可以用于推断该信息。

3. 页面分配

mm_page_alloc           page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s
mm_page_alloc_zone_locked page=%p pfn=%lu order=%u migratetype=%d cpu=%d percpu_refill=%d
mm_page_free            page=%p pfn=%lu order=%d
mm_page_free_batched    page=%p pfn=%lu order=%d cold=%d

这四个事件处理页面分配和释放。 mm_page_alloc 是页面分配器活动的简单指示器。 页面可以从每个 CPU 的分配器(高性能)或伙伴分配器分配。

如果页面直接从伙伴分配器分配,则会触发 mm_page_alloc_zone_locked 事件。 此事件很重要,因为高活动量意味着 zone->lock 上的高活动量。 采用此锁会通过禁用中断、脏化 CPU 之间的缓存行和序列化许多 CPU 来降低性能。

当页面由调用者直接释放时,只会触发 mm_page_free 事件。 此处的大量活动可能表明调用者应该批量处理他们的活动。

当页面批量释放时,也会触发 mm_page_free_batched 事件。 广义上讲,页面从 LRU 锁中批量取出,并使用页面列表批量释放。 此处的大量活动可能表明系统处于内存压力之下,并且还可以指示 lruvec->lru_lock 上的争用。

4. 每个 CPU 的分配器活动

mm_page_alloc_zone_locked     page=%p pfn=%lu order=%u migratetype=%d cpu=%d percpu_refill=%d
mm_page_pcpu_drain            page=%p pfn=%lu order=%d cpu=%d migratetype=%d

在页面分配器前面是一个每个 CPU 的页面分配器。 它仅存在于 order-0 页面,减少了 zone->lock 上的争用,并减少了 struct page 上的写入量。

当每个 CPU 的列表为空或分配了错误类型的页面时,zone->lock 将被获取一次,并且每个 CPU 的列表将被重新填充。 为每个分配的页面触发的事件是 mm_page_alloc_zone_locked,该事件指示它是用于 percpu_refill 还是不用于 percpu_refill。

当每个 CPU 的列表太满时,会释放许多页面,每个页面都会触发 mm_page_pcpu_drain 事件。

事件的单独性质是为了可以在分配和释放之间跟踪页面。 连续发生的许多 drain 或 refill 页面意味着 zone->lock 被获取一次。 大量的每个 CPU 的 refill 和 drain 可能意味着 CPU 之间的不平衡,其中太多的工作集中在一个地方。 这也可能表明每个 CPU 的列表应该更大。 最后,一个 CPU 上的大量 refill 和另一个 CPU 上的 drain 可能是导致大量缓存行反弹的一个因素,如果可以通过一些算法更改在同一 CPU 上分配和释放页面,则值得研究。

5. 外部碎片

mm_page_alloc_extfrag         page=%p pfn=%lu alloc_order=%d fallback_order=%d pageblock_order=%d alloc_migratetype=%d fallback_migratetype=%d fragmenting=%d change_ownership=%d

外部碎片影响高阶分配是否成功。 对于某些类型的硬件,这很重要,尽管在可能的情况下会避免它。 如果系统正在使用巨型页面并且需要在系统的生命周期内调整池的大小,则此值很重要。

大量的此事件意味着内存正在碎片化,并且高阶分配将在未来的某个时间开始失败。 减少此事件发生的一种方法是以 3*pageblock_size*nr_online_nodes 的增量增加 min_free_kbytes 的大小,其中 pageblock_size 通常是默认巨型页面大小的大小。