- 小内存分配, 例如分配 task_struct 对象
- 会调用 kmem_cache_alloc_node 函数, 从 task_struct 缓存区域 task_struct_cachep(在系统初始化时, 由 kmem_cache_create 创建) 分配一块内存
- 使用 task_struct 完毕后, 调用 kmem_cache_free 回收到缓存池中
- struct kmem_cache 用于表示缓存区信息, 缓存区即分配连续几个页的大块内存, 再切成小内存
- 小内存即缓存区的每一项, 都由对象和指向下一项空闲小内存的指针组成(随机插入/删除+快速查找空闲)
- struct kmem_cache 中三个 kmem_cache_order_objects 表示不同的需要分配的内存块大小的阶数和对象数
- 分配缓存的小内存块由两个路径 fast path 和 slow path , 分别对应 struct kmem_cache 中的 kmem_cache_cpu 和 kmem_cache_node
- 分配时先从 kmem_cache_cpu 分配, 若其无空闲, 再从 kmem_cache_node 分配, 还没有就从伙伴系统申请新内存块
- struct kmem_cache_cpu 中
- page 指向大内存块的第一个页
- freelist 指向大内存块中第一个空闲项
- partial 指向另一个大内存块的第一个页, 但该内存块有部分已分配出去, 当 page 满后, 在 partial 中找
- struct kmem_cache_node
- 也有 partial, 是一个链表, 存放部分空闲的多个大内存块, 若 kmem_cacche_cpu 中的 partial 也无空闲, 则在这找
- 分配过程
- kmem_cache_alloc_node->slab_alloc_node
- 快速通道, 取出 kmem_cache_cpu 的 freelist , 若有空闲直接返回
- 普通通道, 若 freelist 无空闲, 调用 `__slab_alloc`
- `__slab_alloc` 会重新查看 freelist, 若还不满足, 查看 kmem_cache_cpu 的 partial
- 若 partial 不为空, 用其替换 page, 并重新检查是否有空闲
- 若还是无空闲, 调用 new_slab_objects
- new_slab_objects 根据节点 id 找到对应 kmem_cache_node , 调用 get_partial_node
- 首先从 kmem_cache_node 的 partial 链表拿下一大块内存, 替换 kmem_cache_cpu 的 page, 再取一块替换 kmem_cache_cpu 的 partial
- 若 kmem_cache_node 也没有空闲, 则在 new_slab_objects 中调用 new_slab->allocate_slab->alloc_slab_page 根据某个 kmem_cache_order_objects 设置申请大块内存
- 页面换出
- 触发换出:
- 1) 分配内存时发现没有空闲; 调用 `get_page_from_freelist->node_reclaim->__node_reclaim->shrink_node`
- 2) 内存管理主动换出, 由内核线程 kswapd 实现
- kswapd 在内存不紧张时休眠, 在内存紧张时检测内存 调用 balance_pgdat->kswapd_shrink_node->shrink_node
- 页面都挂在 lru 链表中, 页面有两种类型: 匿名页; 文件内存映射页
- 每一类有两个列表: active 和 inactive 列表
- 要换出时, 从 inactive 列表中找到最不活跃的页换出
- 更新列表, shrink_list 先缩减 active 列表, 再缩减不活跃列表
- 缩减不活跃列表时对页面进行回收:
- 匿名页回收: 分配 swap, 将内存也写入文件系统
- 文件内存映射页: 将内存中的文件修改写入文件中
展开