Linux 内存分配算法
内存管理算法——对讨厌自己管理内存的人来说是天赐的礼物
1、内存碎片
1) 基本原理
产生原因:内存分配较小,并且分配的这些小的内存生存周期又较长,反复申请后将产生内存碎片的出现
优点:提高分配速度,便于内存管理,防止内存泄露
缺点:大量的内存碎片会使系统缓慢,内存使用率低,浪费大
2) 如何避免内存碎片
少用动态内存分配的函数(尽量使用栈空间)
分配内存和释放的内存尽量在同一个函数中
尽量一次性申请较大的内存,而不要反复申请小内存
尽可能申请大块的 2 的指数幂大小的内存空间
外部碎片避免——伙伴系统算法
内部碎片避免——slab 算法
自己进行内存管理工作,设计内存池
slab 算法——基本原理
1) 基本概念
Linux 所使用的 slab 分配器的基础是 Jeff Bonwick 为 SunOS 操作系统首次引入的一种算法。
它的基本思想是将内核中经常使用的对象放到高速缓存中,并且由系统保持为初始的可利用状态。比如进程描述符,内核中会频繁对此数据进行申请和释放
2) 内部碎片
已经被分配出去的的内存空间大于请求所需的内存空间3) 基本目标
减少伙伴算法在分配小块连续内存时所产生的内部碎片
将频繁使用的对象缓存起来,减少分配、初始化和释放对象的时间开销
通过着色技术调整对象以更好的使用硬件高速缓存
9、内核态内存池
1) 基本原理
先申请分配一定数量的、大小相等(一般情况下) 的内存块留作备用
当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存
这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升
2) 内核 API
mempool_create 创建内存池对象
mempool_alloc 分配函数获得该对象
mempool_free 释放一个对象
mempool_destroy 销毁内存池
DMA 内存
1) 什么是 DMA
直接内存访问是一种硬件机制,它允许外围设备和主内存之间直接传输它们的 I/O 数据,而不需要系统处理器的参与
2) DMA 控制器的功能
能向 CPU 发出系统保持(HOLD)信号,提出总线接管请求
当 CPU 发出允许接管信号后,负责对总线的控制,进入 DMA 方式
能对存储器寻址及能修改地址指针,实现对内存的读写操作
能决定本次 DMA 传送的字节数,判断 DMA 传送是否结束
发出 DMA 结束信号,使 CPU 恢复正常工作状态
3) DMA 信号
DREQ:DMA 请求信号。是外设向 DMA 控制器提出要求,DMA 操作的申请信号
DACK:DMA 响应信号。是 DMA 控制器向提出 DMA 请求的外设表示已收到请求和正进行处理的信号
HRQ:DMA 控制器向 CPU 发出的信号,要求接管总线的请求信号。
HLDA:CPU 向 DMA 控制器发出的信号,允许接管总线的应答信号:
1、内存的使用场景
page 管理
slab(kmalloc、内存池)
用户态内存使用(malloc、relloc 文件映射、共享内存)
程序的内存 map(栈、堆、code、data)
内核和用户态的数据传递(copy_from_user、copy_to_user)
内存映射(硬件寄存器、保留内存)
DMA 内存
2、用户态内存分配函数
alloca 是向栈申请内存,因此无需释放
malloc 所分配的内存空间未被初始化,使用 malloc() 函数的程序开始时(内存空间还没有被重新分配) 能正常运行,但经过一段时间后(内存空间已被重新分配) 可能会出现问题
calloc 会将所分配的内存空间中的每一位都初始化为零
realloc 扩展现有内存空间大小
a) 如果当前连续内存块足够 realloc 的话,只是将 p 所指向的空间扩大,并返回 p 的指针地址。这个时候 q 和 p 指向的地址是一样的
b) 如果当前连续内存块不够长度,再找一个足够长的地方,分配一块新的内存,q,并将 p 指向的内容 copy 到 q,返回 q。并将 p 所指向的内存空间删除
mmap 将一个文件或者其它对象映射进内存,多进程可访问
3、内核态内存分配函数函数分配原理最大内存其他_get_free_pages直接对页框进行操作4MB适用于分配较大量的连续物理内存kmem_cache_alloc基于 slab 机制实现128KB适合需要频繁申请释放相同大小内存块时使用kmalloc基于 kmem_cache_alloc 实现128KB最常见的分配方式,需要小于页框大小的内存时可以使用vmalloc建立非连续物理内存到虚拟地址的映射物理不连续,适合需要大内存,但是对地址连续性没有要求的场合dma_alloc_coherent基于_alloc_pages 实现4MB适用于 DMA 操作ioremap实现已知物理地址到虚拟地址的映射适用于物理地址已知的场合,如设备驱动alloc_bootmem在启动 kernel 时,预留一段内存,内核看不见小于物理内存大小,内存管理要求较高
4、malloc 申请内存
调用 malloc 函数时,它沿 free_chuck_list 连接表寻找一个大到足以满足用户请求所需要的内存块