Linux内核 内存管理(7)vmalloc/VMA/malloc
一.vmalloc
1.vmalloc产生的来由
是用于分配虚拟地址连续但物理地址不连续的内存的函数
| 连续 | 小内存(<128KB) | 内核中需要物理连续的小块内存 |
| 不连续 | 大内存(>PAGE_SIZE) | 大块虚拟连续内存 |
| 连续 | 以页为单位 | 直接操作物理页(如 DMA) |
2.vmalloc实现原理
1)虚拟地址空间 内核虚拟地址空间中,vmalloc 使用一段独立的区域(通常从 VMALLOC_START 到 VMALLOC_END),与内核直接映射的物理内存区域(ZONE_NORMAL)隔离。
2)分配流程 查找虚拟地址空间:在内核的虚拟地址空间中找到一块未被使用的连续区域。 分配物理页:分配多个不连续的物理页(通过 alloc_page 等函数)。 建立页表映射:将分配的物理页映射到连续的虚拟地址空间,更新页表。
3.vmalloc接口函数
https://elixir.bootlin.com/linux/v6.11/source/include/linux/vmalloc.h#L144
二.VMA
1.总览
task_struct: 进程https://elixir.bootlin.com/linux/v6.6/source/include/linux/sched.h#L743
1.mm_struct: 进程的整个虚拟地址空间https://elixir.bootlin.com/linux/v6.6/source/include/linux/mm_types.h#L673
1.1 pgd: 进程的页全局目录(下半部分直接线性映射到内核空间)
1.2 mmap: 内存映射区域基地址 1.3 栈/堆/数据段/代码段 起始地址和终止地址等(最下面那个是命令行参数/环境变量的起始和终止地址)
- 每个区域(代码段、数据段、堆、栈等)在内核中对应一个或多个 VMA(Virtual Memory Area)。
- 例如,堆对应的 VMA 的
vm_start
是start_brk
,vm_end
是brk
。
2.VMA数据结构
https://elixir.bootlin.com/linux/v6.6/source/include/linux/mm_types.h#L565
3.VMA操作函数
https://elixir.bootlin.com/linux/v6.6/source/mm/mmap.c#L1864
find_vma
insert_vm_struct
三.malloc
用户空间主要用来分配虚拟内存的API,用内核术语来说就是分配一块VMA
calloc:动态分配完内存后,自动初始化该内存空间为0,malloc并不初始化,里面是垃圾数据
分配流程:
- 若空闲内存池能满足请求:
- 从空闲内存池中搜索第一个大小大于或等于请求大小的空闲块(采用首次适应算法)。
- 若找到的空闲块大小刚好等于请求大小:将此块从空闲内存池链表中移走,并把该块的地址返回给用户。
- 若找到的空闲块比请求大:从该空闲块的后部取出与请求大小一样的空间分配给用户,前部剩余部分作为新的空闲块留在空闲内存池中,并更新相关空闲块的大小、指针等信息。
- 若空闲内存池不能满足请求:根据请求内存大小 size 判断选择调用 brk 还是 mmap 向系统申请新内存:
- brk 方式(适用于小块内存分配,一般小于 128KB )
- mmap 方式(适用于大块内存分配,大于等于 128KB ,也常用于内存映射文件)
1.malloc是否马上分配物理内存?并不是,是按需分配,看如下malloc流程
实际做法是缺页中断,当进程真的需要访问这些虚拟页面的时候才会去创建物理内存,因为malloc分配的内存并不会马上使用。
如果想直接分配物理内存,就可以直接使用mlock
- 系统调用 mlock 允许程序在物理内存上锁住它的部分或全部地址空间,调用 mlock 会马上为虚拟内存分配物理内存
- 将阻止 Linux 将这个内存页调度到交换空间(swap space)
- 在 brk 函数实现中,最后返回虚拟内存地址的时候,会去检查一个变量 VM_LOCKED,这个 VM_LOCKED 通常从 mlock 系统调用中设置而来如果有,那么需要调用 mm_populate () 马上分配物理内存并建立映射一般情况是:一直将分配物理页面的工作推延到用户进程需要访问这些虚拟页面时,发生了缺页中断才会分配物理内存,并和虚拟地址建立映射关系
2.内存管理中的三个重要函数
3.