本节说明如何查找和检查内核内存高速缓存。通过发出 ::kmastat 命令,可以了解系统中的各种 kmem 高速缓存。
> ::kmastat cache buf buf buf memory alloc alloc name size in use total in use succeed fail ------------------------- ------ ------ ------ --------- --------- ----- kmem_magazine_1 8 24 1020 8192 24 0 kmem_magazine_3 16 141 510 8192 141 0 kmem_magazine_7 32 96 255 8192 96 0 ... kmem_alloc_8 8 3614 3751 90112 9834113 0 kmem_alloc_16 16 2781 3072 98304 8278603 0 kmem_alloc_24 24 517 612 24576 680537 0 kmem_alloc_32 32 398 510 24576 903214 0 kmem_alloc_40 40 482 584 32768 672089 0 ... thread_cache 368 107 126 49152 669881 0 lwp_cache 576 107 117 73728 182 0 turnstile_cache 36 149 292 16384 670506 0 cred_cache 96 6 73 8192 2677787 0 ...
如果运行 ::kmastat,则可以了解“正常”系统的信息。 这将有助于发现系统中正在泄漏内存的过大高速缓存。 根据所运行的系统和正在运行的进程数等因素,::kmastat 的结果将有所不同。
列出各种 kmem 高速缓存的另一种方法是使用 ::kmem_cache 命令:
> ::kmem_cache ADDR NAME FLAG CFLAG BUFSIZE BUFTOTL 70036028 kmem_magazine_1 0020 0e0000 8 1020 700362a8 kmem_magazine_3 0020 0e0000 16 510 70036528 kmem_magazine_7 0020 0e0000 32 255 ... 70039428 kmem_alloc_8 020f 000000 8 3751 700396a8 kmem_alloc_16 020f 000000 16 3072 70039928 kmem_alloc_24 020f 000000 24 612 70039ba8 kmem_alloc_32 020f 000000 32 510 7003a028 kmem_alloc_40 020f 000000 40 584 ...
此命令非常有用,因为它可将高速缓存名称映射到地址,并为 FLAG 列中的每个高速缓存提供调试标志。 必须要了解的是,分配器对调试功能的选择是基于每个高速缓存从这组标志派生而来的。 这些是在创建高速缓存时与全局 kmem_flags 变量一起设置的。在系统运行的同时设置 kmem_flags 不会影响调试行为,但会影响随后创建的高速缓存(这种情况在引导后很少发生)。
接下来,请直接使用 MDB 的 kmem_cache walker 遍历 kmem 高速缓存的列表:
> ::walk kmem_cache 70036028 700362a8 70036528 700367a8 ...
这将产生对应于内核中每个 kmem 高速缓存的指针的列表。 要了解有关特定高速缓存的信息,请应用 kmem_cache 宏:
> 0x70039928$<kmem_cache 0x70039928: lock 0x70039928: owner/waiters 0 0x70039930: flags freelist offset 20f 707c86a0 24 0x7003993c: global_alloc global_free alloc_fail 523 0 0 0x70039948: hash_shift hash_mask hash_table 5 1ff 70444858 0x70039954: nullslab 0x70039954: cache base next 70039928 0 702d5de0 0x70039960: prev head tail 707c86a0 0 0 0x7003996c: refcnt chunks -1 0 0x70039974: constructor destructor reclaim 0 0 0 0x70039980: private arena cflags 0 104444f8 0 0x70039994: bufsize align chunksize 24 8 40 0x700399a0: slabsize color maxcolor 8192 24 32 0x700399ac: slab_create slab_destroy buftotal 3 0 612 0x700399b8: bufmax rescale lookup_depth 612 1 0 0x700399c4: kstat next prev 702c8608 70039ba8 700396a8 0x700399d0: name kmem_alloc_24 0x700399f0: bufctl_cache magazine_cache magazine_size 70037ba8 700367a8 15 ...
用于调试的重要字段包括 'bufsize'、'flags' 和 'name'。kmem_cache 的名称(在本示例中为 "kmem_alloc_24")指明了它在系统中的用途。 Bufsize 表示此高速缓存中每个缓冲区的大小;在本示例中,高速缓存用于进行大小为 24 或更小的分配。 'flags' 指明了为此高速缓存启用的调试功能。 可以找到在 <sys/kmem_impl.h> 中列出的调试标志。 在本示例中 'flags' 为 0x20f,即 KMF_AUDIT | KMF_DEADBEEF | KMF_REDZONE | KMF_CONTENTS | KMF_HASH。 本文档将在后续几节中说明每个调试功能。
如果有兴趣查看特定高速缓存中的缓冲区,可以直接遍历该高速缓存中已分配和已释放的缓存区:
> 0x70039928::walk kmem 704ba010 702ba008 704ba038 702ba030 ... > 0x70039928::walk freemem 70a9ae50 70a9ae28 704bb730 704bb2f8 ...
MDB 提供了一种为 kmem walker 提供高速缓存地址的快捷方式:此方式会为每个 kmem 高速缓存提供特定的 walker,并且 walker 与高速缓存同名。例如:
> ::walk kmem_alloc_24 704ba010 702ba008 704ba038 702ba030 ... > ::walk thread_cache 70b38080 70aac060 705c4020 70aac1e0 ...
现在,您已经知道如何迭代内核内存分配器的内部数据结构以及如何检查 kmem_cache 数据结构的最重要成员。