分类: 嵌入式
2018-10-07 20:48:44
Linux虚拟内存空间的一般布局(其实I/O空间也在其中,通常占用高端内存空间,在此未标出)
从PAGE_OFFSET到high_memory之间的地址空间,是系统物理内存映射区,它所映射物理内存就是低端内存。低端内存在Linux地址空间中始终有永久的一一对应的虚拟地址(包括物理内存中的孔洞也是一一对应的),系统初始化过程中将低端内存永久映射到了内核虚拟地址空间的系统物理内存映射区,为低端内存建立了虚拟映射页表。低端内存内物理内存的物理地址与虚拟地址之间的转换可以通过__pa(x)和__va(x)两个宏来进行,__pa(x)将内核虚拟地址空间的地址x转换成对应的物理地址,相当于__virt_to_phys((unsigned long)(x)),__va(x)则相反,把低端物理内存空间的地址转换成对应的内核虚拟地址,相当于((void *)__phys_to_virt((unsigned long)(x)))。
低端内存地址之上的物理内存是高端内存,高端内存在Linux地址空间中没有没有固定的一一对应的内核虚拟地址,系统初始化过程中不会为这些内存建立映射页表将其固定映射到Linux地址空间,而是需要使用高端内存的时候才为分配的高端物理内存建立映射页表,使其能够被内核使用,否则不能被使用。高端内存的物理地址于现行地址之间的转换不能使用上面的__pa(x)和__va(x)宏。
高端内存的由来:
Linux将4GB的地址空间划分成两部分,从0x00000000到0xBFFFFFFF共3GB虚拟地址空间作为用户空间由用户进程独占,这部分虚拟地址空间并没有固定映射到物理内存空间上;从0xC0000000到0xFFFFFFFF的第4GB虚拟地址空间作为内核空间,在嵌入式系统中,这部分虚拟地址空间除了映射物理内存空间之外还要映射处理器内部外设寄存器空间等I/O空间。0xC0000000~high_memory之间的内核虚拟地址空间专用来固定映射系统中的物理内存,也就是说0xC0000000~high_memory之间空间大小与系统的物理内存空间大小是相同的(当然在配置了CONFIG_DISCONTIGMEMD选项的非连续内存系统中,内核虚拟地址空间和物理内存空间一样可能存在内存孔洞),如果系统中的物理内存容量远小于1GB,那么内核现行地址空间中内核虚拟地址空间之上的high_memory~0xFFFFFFFF之间还有足够的空间来固定映射一些I/O空间。可是,如果系统中的物理内存容量(包括内存孔洞)大于1GB,那么就没有足够的内核虚拟地址空间来固定映射系统全部物理内存以及一些I/O空间了,为了解决这个问题,在x86处理器平台设置了一个经验值:896MB,就是说,如果系统中的物理内存(包括内存孔洞)大于896MB,那么将前896MB物理内存(低端内存)固定映射到内核虚拟地址空间0xC0000000~0xC0000000+896MB(=high_memory)上,而896MB之后的物理内存则不建立到内核虚拟地址空间的固定映射,这部分内存就叫高端物理内存。此时内核虚拟地址空间high_memory~0xFFFFFFFF之间的128MB空间就称为高端内存线性地址空间,用来映射高端物理内存和I/O空间。896MB是x86处理器平台的经验值,留了128MB虚拟地址空间来映射高端内存以及I/O地址空间,我们在嵌入式系统中可以根据具体情况修改这个阈值,比如,MIPS中将这个值设置为0x20000000B(512MB),那么只有当系统中的物理内存空间容量大于0x20000000B时,内核才需要配置CONFIG_HIGHMEM选项,使能内核对高端内存的分配和映射功能。
高端线性地址空间:
从high_memory到0xFFFFFFFF之间的虚拟地址空间属于高端线性地址空间,其中VMALLOC_START~VMALLOC_END之间虚拟地址被vmalloc()函数用来分配物理上不连续但虚拟地址空间连续的高端物理内存,或者被vmap()函数用来映射高端或低端物理内存,或者由ioremap()函数来重新映射I/O物理空间。PKMAP_BASE开始的LAST_PKMAP(一般等于1024)页虚拟地址空间被kmap()函数用来永久映射高端物理内存。FIXADDR_START开始的KM_TYPE_NR*NR_CPUS页虚拟地址空间被kmap_atomic()函数用来临时映射高端物理内存,其他未用高端线性地址空间可以用来在系统初始化期间永久映射I/O地址空间。