03.linux内存管理

linux内存管理分为 物理内存管理虚拟内存管理两大类。


物理内存定义方面,引入概念:
1、内存节点(node,同一内存节点具有相同访问特性 eg.访问速度,一致内存访问UMA模型只有一个内存节点,非一致内存访问NUMA模型含多个内存访问节点)
2、内存区域(zone,ZONE_DMA为某些DMA设备优先使用的低端内存区域、ZONE_NORMAL可以为内核虚拟地址永久线性映射的一段区域、ZONE_HIGHMEM的区域需要内核虚拟地址空间临时建立页映射关系)。注意可以直接access物理地址的内核虚拟地址空间的大小为1GB。
3、内存页(page,linux物理内存管理的最小单位)
 
物理内存分配方面,使用 page *alloc_pages(gfp_mask,order) 来分配2^order个连续物理页面(gfp_mask控制从哪个ZONE分配,可否允许睡眠等行为)。
1、若物理内存来自ZONE_NORMAL/ZONE_DMA,则使用page_address()简单获取 page * 对应的虚拟地址(映射关系早已建立,so不用再关心)。内核提供了__get_free_pages()=alloc_pages()+page_address()。
2、若物理内存来自 ZONE_HIGHMEM,则需借助kmap()完成 "在动态映射区or固定映射区分配虚拟地址、建立此虚拟地址和物理page的映射" 操作。kunmap()用于解除映射关系,kmap()可能导致睡眠。
 
使用alloc_pages()分配物理内存的几个问题:
1、过于底层,使用不方便。2、对一个structure来说4KB太大了 3、效率也一般
 
于是linux引入slab伙伴系统(有点类似  for特定数据结构的 内存池),当然slab最终还是要调alloc_pages()完成最最底层的物理pages分配。
1、一些常用内核struct的分配和释放(包括内定义的内核struct)。通过全局量 cache_cache (顶层 struct kmem_cache)管理。
2、内核系统调用kmalloc()(这个函数不支持从 ZONE_HIGHMEM 分配)也是基于slab。通过全局数组 struct cache_sizes malloc_sizes[] 管理。

vmalloc()的实现:
1、从vmalloc区分配一段虚拟地址(会多分配一点,对齐到 page。这里虚拟地址区块 vm_area_struct 的管理会用到红黑树)
2、调用 alloc_pages() 分配物理pages(并不是连续分配,而是一个page一个page分配,即vmalloc可不保证物理pages是连续的,同时会优先从ZONE_HIGHMEM分配,因为其他两个 ZONE很宝贵,留给kmalloc这类需要连续物理页面的函数使用)。
3、建立物理pages和虚拟地址间的映射关系。(多分配的那一点虚拟地址并不映射,故意留一个空洞,因此只要你敢越界,kernel就报未映射异常)
 
ioremap()的实现:
1、从vmalloc区分配一段虚拟地址。
2、将这块虚拟地址和设备IO地址建立映射关系。
(因此可以看出ioremap是将内核空间虚拟地址映射到设备内存)
 
mmap()的实现(深入linux设备驱动内核机制 ch10):
1、参数检查(eg.确保要映射的长度是page对齐的)
2、在用户虚拟地址空间找到一块满足长度的虚拟地址块(如果用户有指定addr,那就使用用户的。否则自动找一个作为最终返回的虚拟地址)
3、malloc一个vm_area_struct用于管理上面的那个虚拟地址块,并调用驱动函数的mmap方法
4、驱动函数mmap方法中使用remap_pfn_range()接口完成"虚拟地址块到物理地址的页表映射"(页目录项和页表项的填充,至于每次映射的长度,物理地址不连续的可能要按页调用remap_pfn_range)
(因此mmap是将用户空间虚拟地址映射到设备内存)
 
用户层接口malloc实现:
通过 brk() + mmap()

猜你喜欢

转载自blog.csdn.net/ddddfang/article/details/88943123