kmalloc

1.作用

  • 用于在kernel中为object申请小块的内存(主要是小于4K的内存,大于4K的也有),底层就是调用了slab(也会用buddy system)。
  • 虚拟连续,物理也连续。

2. 函数使用说明

参数:

  • size:要申请的内存的大小(2^N对齐)
  • flags:是GFP flag中的一个,表示应该分配什么样的内存。

kmalloc会根据申请的内存大小来决定来决定使用slub还是buddy system进行内存分配。 控制kmalloc分配行为的主要有如下三个宏:

macro

desc

size

KMALLOC_MAX_SIZE

kmalloc可以分配的最大内存,超过此大小时返回NULL。

kmalloc最大可以分配1024个页面,就是buddy system所管理的最大内存块

2 ^ (MAX_ORDER + PAGE_SHIFT - 1)

一般:4K * 1024页 = 4M

KMALLOC_MAX_CACHE_SIZE

kmalloc使用slab分配器分配的最大内存,超过此大小后会使用buddy system

一般:2页* 4K = 8K

KMALLOC_MIN_SIZE

kmalloc可以分配的最小内存,小于此大小时,kmalloc内部会按此大小分配

8 byte

 

kmalloc分配内存的策略

  • 大于8K,从buddy system分配。
  • 小于8K,从slab分配。

3. kmalloc中VA和PA间的映射

kmalloc 分配内存是从直接映射区域(Direct Mapping Area)中拿的,Direct Mapping Area中的内存在内核启动阶段就做好了PA -> VA的线性映射。

PA=VA−Fixed Offset

内核在启动阶段使用kernel_physical_mapping_init函数建立DMA区域的线性映射。

setup_arch

init_mem_mapping

memory_map_top_down

init_range_memory_mapping

init_memory_mapping

kernel_physical_mapping_init

4. 代码实现

1. kmalloc

定义在include/linux/slab.h中

主要是实现在kmalloc_noprof中:

(1)判断size是否是编译时常量,是的话可以减少一些判断,来优化性能。

PS:

编译时常量:在编译时就能确定它的值的常量,直接硬编码到二进制。

运行时常量:在运行时才能知道它的值的,占用内存,适用于动态场景。

(2)如果是编译时常量,且size> KMALLOC_MAX_CACHE_SIZE(PAGE_SIZE*2),走__kmalloc_large_node_noprof(buddy system)。

(3)否则(size>PAGE_SIZE*2)走__kmalloc_cache_node_noprof(Slub)。

(4)如果是非编译常量:走__kmalloc_node_noprof。

调用的流程总结为:

总结:不管是走编译常量分支,还是非编译常量分支,最后调用的函数是一样的。

  • 大于PAGE_SIZE*2调用___kmalloc_large_node。
  • 小于PAGE_SIZE*2调用slab_alloc_node。

所以我们分别研究这两个函数。

 

2. Buddy申请:___kmalloc_large_node

为了避免不必要的开销,大内存分配请求直接通过页分配器(page allocator)处理。

使用了 __GFP_COMP ,因为在 kfree 时需要知道分配的内存页的阶数(order),以便正确释放内存页。

PS:我理解compound page(复合页)就是,物理上连续的页,把N个连续的页视为一个大页,一个整体。

(1)真正的分配内存函数。从指定的node上分配内存,这里传的node值是NUMA_NO_NODE,没有指定任何node。

alloc_pages_node_noprof接下来的调用栈如下,最后调用到的__alloc_frozen_pages_noprof就是buddy system分配器的核心实现了,具体解析见Buddy System

(2)通过folio_address获取分配的内存页的起始地址,并更新LRU统计信息,标记分配的内存也为不可回收的slab内存(这部分不懂,LRU待学)。

(3)使用KASAN和KMSAN检查大内存分配(这部分不懂,待学)。

3. slab申请:slab_alloc_node

3.1 kmalloc slab cache定义:kmalloc_info[]

内核为kmalloc创建了不同尺寸的通用slab cache(kmem_cache),可以通过 cat /proc/slabinfo 命令来查看:

这些不同尺寸的slab cache信息都存储在称为kmalloc_info[]struct kmalloc_info_struct结构体中。

struct kmalloc_info_struct定义在mm/slab.h中:

  • name:该slab cache的名称,kmalloc-xxx
  • size:该slab cache的内存块的大小。

kmalloc_info[]的初始化定义:

所以kmalloc_info[]的内容如下图:

kmalloc_info[] 数组中的 index 有一个特点,从 index = 3 开始一直到数组的最后一个 index,index是该slab cache中object的size的order:

object size = 2^index

 

内核中,对于内存块的申请需求大部分情况下都在 96 字节或者 192 字节附近,所以把这两个size特殊的放在index 1和2的位置。
 

3.2 kmalloc_caches

内核启动初始化的时候会根据kmalloc_info中的内容,通过create_kmalloc_caches函数,挨个建立kmem_cache,并把所有的kmem_cache结构体使用kmalloc_caches变量统一管理。

kmalloc_caches定义在mm/slab_common.c中:

  • kmem_buckets表示一个长度为KMALLOC_SHIFT_HIGH + 1的kmem_cache指针数组。
  • 所以其实等于struct kmem_cache *kmalloc_caches[NR_KMALLOC_TYPES][KMALLOC_SHIFT_HIGH + 1]

type

kmalloc_cache_type定义在include/linux/slab.h中:

  • KMALLOC_NORMAL: kmalloc 需要从 ZONE_NORMAL 物理内存区域中分配内存。
  • KMALLOC_DMA: kmalloc 需要从 ZONE_DMA 物理内存区域中分配内存。
  • KMALLOC_RECLAIM:需要分配可以被回收的内存
  • etc.

index

kmalloc从slub分配内存的内存大小范围由 KMALLOC_SHIFT_LOW KMALLOC_SHIFT_HIGH 决定,它们被定义在 /include/linux/slab.h中:

kmalloc支持的最小内存块为:2^KMALLOC_SHIFT_LOW = 2^3 = 8 byte。

kmalloc支持的最大内存块为:2^KMALLOC_SHIFT_HIGH = 2^(12+1) = 8K = PAGE_SIZE * 2

 

create_kmalloc_caches:

轮询kmalloc的type和index,挨个调用new_kmalloc_cache。

new_kmalloc_cache:

根据输入的idx和type,从预先写好的kmalloc_info中拿name和size,调用create_kmalloc_cache建立kmem_cache并把指针放入kmalloc_caches

总结:

kmalloc能从slub分配的内存块大小在8 byte ~ 8K 之间:

3.3 计算分配的大小:kmalloc_slab

创建了 不同大小的kmalloc slab cache,如何计算取最佳尺寸的 slab cache呢?

kmalloc_slab函数。

总结:

kmalloc_slab定义在mm/slab.h中:

(1)根据输入参数,确定kmalloc的type,大多数情况下是KMALLOC_NORMAL。调用kmalloc时,通过gfp flag指定kmalloctype

(2)如果size<=192,就通过kmalloc_size_index获得index。

(3)如果size>192,就通过fls计算index

kmalloc_size_index:

就是一个数组,快速的检索。定义在mm/slab_common.c:

fls

"Find Last Set" 函数,用于返回一个整数中最高有效位的位置(从1开始计数)

eg:fls(8) = fls(1000) =  4

3.4 slab分配函数:slab_alloc_node

知道了从哪个kmalloc kmem cache中分配内存后,就是分配内存操作了。

通过调用slab_alloc_node。这个是slab实现的分配函数,详情见Slab笔记

kfree

内核提供了 kfree 函数来释放由 kmalloc 内存池分配的内存块,参数 x 表示释放内存块的虚拟内存地址

定义在mm/slub.c中:

(1)把虚拟地址转换为物理地址,并且返回一个folio结构体(方便管理复合页)

(2)如果内存当时不是从slab分配的,调用free_large_kmalloc释放回buddy system。

(3)如果是从slab分配的,调用slab_free释放回slab中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值