子系统跟踪点:kmem

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

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

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

  • 页分配

  • Per-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 是页分配器活动的简单指标。页可以从 Per-CPU 分配器(高性能)或伙伴分配器(buddy allocator)中分配。

如果直接从伙伴分配器分配页,则会触发 mm_page_alloc_zone_locked 事件。此事件很重要,因为大量的活动意味着 zone->lock 上的活动频繁。获取此锁会通过禁用中断、脏化 CPU 间的缓存行以及串行化多个 CPU 来降低性能。

当页由调用者直接释放时,会触发唯一的 mm_page_free 事件。此处大量的活动可能表明调用者应该批量处理其操作。

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

4. Per-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

页分配器前端是一个 per-cpu 页分配器。它仅针对 order-0 页存在,减少了对 zone->lock 的争用,并减少了对 struct page 的写入量。

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

当 Per-CPU 列表太满时,一些页会被释放,每一页都会触发 mm_page_pcpu_drain 事件。

这些事件的独立特性是为了可以在分配和释放之间跟踪页。连续发生的一系列 drain 或 refill 页意味着 zone->lock 被获取了一次。大量的 Per-CPU refill 和 drain 可能意味着 CPU 之间存在不平衡,即太多的工作集中在一个地方。这也可能表明 Per-CPU 列表的大小应该更大。最后,一个 CPU 上大量的 refill 和另一个 CPU 上的 drain 可能是导致 CPU 间写入引起大量缓存行抖动(cache line bounces)的一个因素,如果可以通过算法更改在同一个 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

外部碎片会影响高阶(high-order)分配是否成功。对于某些类型的硬件,这一点很重要,尽管在可能的情况下应避免这种情况。如果系统正在使用大页(huge pages)并需要在系统生命周期内调整池的大小,则该值非常重要。

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