如下,我们在 slabinfo 能够看到 kmalloc 分成 4 类,分别 cgroup, dma, reclaim, normal 的 kmalloc 版本,其中 kmalloc-xxx 代表默认 normal kmalloc,也就是 在内存压力大时,不能够被回收的内存。kmalloc-rcl-xxx 代表在内存压力大时,能够回收 这部分内存。
Copy $ cat /proc/slabinfo
kmalloc-cg-xxx
dma-kmalloc-xxx
kmalloc-rcl-xxx
kmalloc-xxx 在系统启动时,创建各个版本的 kmalloc 并且初始化,其中会将 reclaim kmalloc 的 s->flags 会存储 SLAB_RECLAIM_ACCOUNT 标志。如下:
Copy kmem_cache_init ()
create_kmalloc_caches ( KMALLOC_NORMAL , KMALLOC_RECLAIM )
new_kmalloc_cache ()
if is KMALLOC_RECLAIM , set SLAB_RECLAIM_ACCOUNT to s -> flags 当调用默认 kmalloc() 分配内存,代表 KMALLOC_NORMAL,不能够被回收的内存。将分配 的内存大小统计到 NR_SLAB_UNRECLAIMABLE_B,即 /proc/meminfo SUnreclaim 字段。
当调用 kmalloc(__GFP_RECLAIMABLE) 分配内存,代表 KMALLOC_RECLAIM,能够被回收的内存, s->flags 有 SLAB_RECLAIM_ACCOUNT 标志,将分配的内存大小统计到 NR_SLAB_RECLAIMABLE_B, 即 /proc/meminfo SReclaimable 字段。
如果分配内存大小超过 8KB 以上,以上两种类型的 kmalloc 都调用直接 __kmalloc_large_node() 将分配的内存大小统计到 NR_SLAB_UNRECLAIMABLE_B, 即 /proc/meminfo SUnreclaim 字段。
Copy kmalloc ()
__do_kmalloc_node ()
__kmalloc_large_node ()
lruvec_stat_mod_folio ( NR_SLAB_UNRECLAIMABLE_B )
kmalloc_slab ()
kmalloc_type ()
if have __GFP_RECLAIMABLE , return KMALLOC_RECLAIM
slab_alloc_node ()
new_slab ()
allocate_slab ()
cache_vmstat_idx ()
mod_node_page_state ( if s -> flgas have SLAB_RECLAIM_ACCOUNT ,
return NR_SLAB_RECLAIMABLE_B ,
else NR_SLAB_UNRECLAIMABLE_B ) 只有调用 kmem_cache_create(SLAB_RECLAIM_ACCOUNT) 创建 kmem_cache 时, kmem_cache的 s->flags 才会存储 SLAB_RECLAIM_ACCOUNT 标志,并且 s->allocflags 才会存储 __GFP_RECLAIMABLE 标志。
当调用 kmem_cache_alloc() 时, 如果 kmem_cache s->flags 有 SLAB_RECLAIM_ACCOUNT 标志,将分配的内存大小统计到 NR_SLAB_RECLAIMABLE_B,即 /proc/meminfo SReclaimable 字段。
否则,将分配的内存大小统计到 NR_SLAB_UNRECLAIMABLE_B,即 /proc/meminfo SUnreclaim 字段。
注意: __GFP_RECLAIMABLE 对 page 分配器是无效的,只对 slab 分配器有效并且 kmalloc(size <= 8KB)。
Q:直接调用 page 分配器接口 alloc_pages() 而分配出来的物理页, 为什么都是不能够被回收的?
由于直接调用 page 分配器而分配出来的物理页,没有相关的链表 list 进行管理,所以 不能够被回收。slab 分配器调用 page 分配器来分配物理页,slab 分配页会对分配出来 的物理页,通过链表 list 进行管理,所以能够被回收。