Linux kernel High memory

来源:互联网 发布:电商跟淘宝有区别吗 编辑:程序博客网 时间:2024/06/02 23:48

在32bit机器上,由于32bit地址空间的限制,最大寻址范围为2^32 - 1也就是4G的线性地址空间,linux 会按照3G/1G, 2G/2G, 1G/3G的分配方式,把这个4G的地址空间划分为两个部分

所谓3G/1G就是3G应用空间 1G内核空间, 2G/2G就是2G的应用地址空间 2G的内核地址空间。


我们这里只考虑3G/1G的情况,即只有1G的内核空间。linux 内核又对这1G的内核地址空间进行了划分,

1. 从PAGE_OFFSET开始的第一段空间为物理RAM的线性映射空间,这段空间的大小在x86机器上固定为896MB,而对于2G/2G划分的arm机器,这个线性映射空间就可能达到1G以上,能够直接映射到这个线性空间的物理地址是DMA zone和Normal Zone,在这个范围之外的物理内存则划归High memory Zone。

2. kernel在PAGE_OFFSET和VMALLOC_START之间保留了一段安全区,目的是为了捕获内存越界

3. VMALLOC_START到VMALLOC_END这段空间是非连续内存区,被vmalloc和vfree使用,非连续内存区可以为内核提供逻辑地址连续但物理地址不一定连续的内存区。非连续内存区即可以映射Normal Zone也可以映射High memory Zone的物理内存。当内核线程需要一大块内存,但是又不需要对应的物理内存是连续的时,可以使用vmalloc分配。由于vmalloc仅仅分配了一段地址空间,并未实际分配物理内存,因此可分配的空间大小可以大于物理内存大小。


当系统物理内存较大时,会有部分物理内存无法进行线性映射,这些无法映射的物理内存称为High memory,相应的可以映射的低端物理内存称为Low memory.

没有线性地址的高端内存中的页框无法被内核访问。线性地址空间后面的一部分专门用于高端物理内存的映射,当然这种映射是暂时的,否则,会造成只有部分高端物理内存能够被映射。

内核可以采用3种不同的机制映射高端物理内存,分别为: 永久内核映射,固定映射(临时内核映射是固定映射的特殊形式)和非连续内存分配


非连续内存分配

非连续内存区即可以映射高端内存也可以映射低端内存。通过连续的线性地址来访问非连续的页框这样一种分配模式很有意义,因为这种模式避免了外碎片,外碎片的存在会使系统没有足够大的连续页框。

linux在几个方面使用非连续内存分配:

1. 为活动的交换区分配数据结构

2. 为模块分配空间

3. 或者给某些I/O驱动程序分配缓冲区,这些驱动不要求连续的物理页框。


永久内核映射 Persistent Kernel Map

允许内核建立高端页框到内核地址空间的长期映射,他们使用主内和页表中的一个专门的页表,地址存放在pkmap_page_table中。页表的大小为512或者1024,这取决于PAE是否被激活,PAE未被激活时 页表项占用4个字节,这样一个页表可以存放1024个页表项,如果PAE被激活,那么页表项占用8字节,这个页表只能存放512项。因此内核一次最多只能访问2M或者4M的高端内存。


固定内核映射

固定内核映射的线性地址类似于0xffffc000.  固定内核映射线性地址到物理地址转换并不像内核线性映射空间直接减去0xc0000000, 而是可以任意方式建立. 也就是说固定映射的线性地址可以对应任意的物理地址.

每一个固定映射的线性地址都对应一个整形索引值,这个整形索引值的定义为

enum fixed_addresses {#ifdef CONFIG_X86_32    FIX_HOLE,    FIX_VDSO,#else    VSYSCALL_LAST_PAGE,    VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE                + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1,    VSYSCALL_HPET,#endif#ifdef CONFIG_X86_32    FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */    FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,#endif    __end_of_fixed_addresses};

fix_to_virt函数计算从给定索引 对应的常量线性地址

static inline unsigned long fix_to_virt(const unsigned int idx){    if (idx >= FIX_KMAP_END)        __this_fixmap_does_not_exist();    return __fix_to_virt(idx);}

为了把一个固定地址和物理映射的线性地址关联起来,内核使用set_fixmap(idx,phys) 和set_fixmap_nocache


临时内核映射
临时内核映射是固定地址内核映射的特殊形式,可以用在中断处理程序和可延迟函数的内部,因为他们不阻塞当前进程。

在高端内存的任何一个页框都可以通过一个窗口映射到内核地址空间.

每个CPU都有自己的一组窗口的集合,具体每个窗口定义参看km_type. 该数据结构中的每个符号,标识了一个窗口,对应着一个固定的内核地址,通过

fix_to_virt 从这个符号(索引)获取内核线性地址。记住每个符号都有一个具体的含义,只能有一种内核成分使用,因此也就保证了临时内核映射可以是非阻塞的



原创粉丝点击