理论概述:
内核把物理页作为内存管理的基本单位;使用struct page结构来描述当前机器上的每一个物理页;比如内核支持4KB的页大小,机器上有1GB物理内存,那么意味着会有262144个struct page;
内核把物理页划分为不同的区;同一区内的内存具有相似的特征;一般会有ZONE_DMA/ZONE_NORMAL/ZONE_HIGHEM;比如X86上会有三个区来划分物理内存;但ARM就没有那么多;
每个区都是用一些策略来管理区内的物理页,Linux使用buddy system来管理区内的物理页,buddy的策略就是将区内所有的物理页划分为1,2,4,8,16,32,64,128,256,512,1024个连续页块;每个区都采取这样的方式管理内部的物理页;这种方式可以解决外碎片问题;
再上一级,就是zoned page frame allocator;也就是提供alloc_page()系列接口部分;
再上一级,就是slab/slob/slub部分,这块就是高速缓存,比如我们常用的kmalloc()接口,就是使用的高速缓存,它是由
32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072个字节的高速缓存组成的,每个字节块一个高速缓存描述符;这个是用来解决内碎片;
再上一级,就是kmalloc;
再有一点,就是内存分配和建立映射,是两个概念,只是我们常用的分配函数已经完成了两个过程;再有就是kmalloc属于内核分配内存的函数,它返回的虚拟地址肯定是在内核空间;
再提一下vmalloc函数,它也是用在内核中用来分配内存的函数,它与kmalloc的主要区别就是,它分配的内存空间在虚拟地址空间上是连续的,而在物理地址空间不一定是连续的;这块理解呢,也可以按照内存分配和建立映射两块来分析;首先分配多个不连续的struct page,然后一个一个的建立到一个连续的地址空间的映射;
再说说高端内存,也就是ZONE_HIGHEM区内的物理内存,由于处理器的内存空间的限制,这部分区域不能够像ZONE_NORMAL区内的物理内存那样,直接映射到内核的虚拟地址空间(说白了就是3GB~4GB这一段虚拟地址空间不够用了,因为SOC上的很多外设控制器也要直接映射一部分物理地址到这1G的范围内,还有一些预留的部分,比如VMALLOC_START-VMALLOC_END区间,永久映射区间,固定映射区间等,这块理解需要将物理内存和内核的虚拟地址空间区分开发,不可混为一谈),但并不意味着不能映射到其他其他地方,比如说映射到进程的地址空间,因为你可以非常容易的从高端内存分配物理内存,然后建立到进程地址空间的映射;这里强调一下,高端内存的划分依据是内核不方便使用,仅此而已;如果要使用高端内存,那么就意味着需要分别的从ZONE_HIGHEM区内分配物理页,以及建立映射,这块,处理器的内存空间会保留一块小区域来建立这种映射;当然,不用的时候是需要取消映射的;
以上说的都是内核空间的内存概念;接着聊进程地址空间的内存问题;
struct page、ZONE_*、buddy system概念应该都是一样的,因为这个是基础;然后使用alloc_page来分配物理页,然后就是进程地址空间的映射问题,映射完成后,进程在自己的地址空间内就可以直接访问这块物理内存了;这块可能引出其他问题,就是比如进程想分配1MB物理内存并映射进它的地址空间,内核里边不会这么傻,它可能只分配4KB,也就是1页物理内存,然后在进程地址空间内中创建1MB有效区域struct vm_area_struct,然后只建立这4KB的映射;然后当需要用到超过着个4KB时,MMU一转,发现没有页表指定,那么就使用缺页来处理呗;
在聊到进程地址空间问题时,我突然想到,从进程用户空间,切换到内核空间,这块地址怎么处理?这个问题就是个脑子一时短路问题;进程的地址空间中,就说32bit,0~3GB是用户空间,3GB~4GB是内核空间,首先地址上不冲突,其次,是否表示进程的地址空间中,已经包含了3GB~4GB的映射,只是访问权限不够而已,我想,是这样的;
再说说malloc函数,它调用的最终结果就是在进程的地址空间建一个有效区域struct vm_area_struct,这个表明进程对这个区域的访问时合法的,但是有可能不存在物理内存的映射,这时就得靠缺页机制了;
这些都只是理论知识,还么有涉及到建立映射用的页表,很多具体东西都需要分析代码,但是,这些问题是可以一览内存子系统的概况;
代码分析:
未完待续。。。