《深入Linux内核架构》P144页。读书存疑,继而想通。
> 铺垫
虚拟地址空间一般按3:1划分为进程地址空间和内核地址空间,32位机器4G的虚拟地址空间就有1G分为内核地址空间。
(1) 内核地址空间前896M是直接映射的物理页帧,即物理内存上的896M能直接映射(通过线性偏移0xC000000)到内核地址空间的这个区域。
(2) 然后是一小段内存间隙用于内核故障报错。
(3) 然后是vmalloc区域,用于动态分配,内核自身会尽力避免非连续的物理地址,这段内存区主要用于动态加载模块时。
(4) 然后是2个页帧的保护性间隔内存。
(5) 然后是持久映射区,用于将highmemory中的非持久页映射到内核中。(存疑,暂不求甚解)
(6) 最后是固定映射区,一直到虚拟地址4GB处。对于上面所述的直接映射区,虚拟地址平移__PAGE_OFFSET(IA-32上即为0xC000000)即得到物理地址,而固定映射不同,这个区域中的虚拟地址指向随机的物理地址,虚拟地址和物理地址的关联可以自由定义但定义后不能改变。固定映射的优点是:1. 编译时对此类地址的处理类似常数,内核一启动即为其分配了物理地址。 2. 此类地址的反引用比普通指针要快。 3. 内核确保在上下文切换时,对应于固定映射的页表项不会从TLB刷出,在访问固定映射的内存时,总是通过TLB高速缓存取得对应的物理地址。
> 正文
对每个固定映射地址都会创建一个常数,加入到fixed_addresses枚举值列表中,__end_of_fixed_addresses是最后一个枚举成员,定义了最大的可能数字。 linux_kernel_2.6.27.62/include/asm-x86/fixmap_32.h :: fix_to_virt()函数用于根据固定映射的fixed_addresses常数计算虚拟地址
static __always_inline unsigned long fix_to_virt(const unsigned int idx) { if(idx >= __end_of_fixed_addresses) __this_fixmap_does_not_exist(); return __fix_to_virt(idx); }