Linux 内核空间虚拟地址和物理地址相互转换

个人学习笔记,可能会有错误之处,敬请谅解。

一直以来感觉虚拟地址和物理地址之间的相互转换非常麻烦,虚拟地址到物理地址的转换由CPU硬件完成,但物理地址到虚拟地址怎么转换啊?况且有时候在软件上也希望通过一个虚拟地址得到物理地址,这时候自然不能依赖CPU的硬件了,该怎么转换呢?

今天看了下Linux代码,才发现内核地址空间的地址转换其实非常简单,总结如下(Kernel: 4.5, x86_64):

物理地址到虚拟地址转换由宏 __va(x) 实现,该宏定义在arch/x86/include/asm/page.h中定义如下:

#define __va(x)                 ((void *)((unsigned long)(x)+PAGE_OFFSET))

怎么样,够简单吧? 就是加上一个offset而已。宏PAGE_OFFSET在arch/x86/include/asm/page_types.h中定义如下:

#define PAGE_OFFSET             ((unsigned long)__PAGE_OFFSET)
宏__PAGE_OFFSET在arch/x86/include/asm/page_64_types.h中定义如下:

#define __PAGE_OFFSET           _AC(0xffff880000000000, UL)
__AC也是宏定义在include/uapi/linux/const.h中:

#ifdef __ASSEMBLY__
#define _AC(X,Y)        X
#define _AT(T,X)        X
#else
#define __AC(X,Y)       (X##Y)
#define _AC(X,Y)        __AC(X,Y)
#define _AT(T,X)        ((T)(X))
#endif
这是为了适应Assembly和C不同的语法定义的宏,可能这就是宏 __AC 名字的来源吧。

到这,从物理地址到虚拟地址的转换就完成了,其实就是加上一个大小为 0xffff880000000000 的偏移量而已,简直太简单了。

可想而知,虚拟地址到物理地址的转换应该也很简单吧,就把这个偏移量减去就行了呗。。。

在arch/x86/include/asm/page.h中由该宏定义:

#define __pa(x)         __phys_addr((unsigned long)(x))

__phys_addr 的定义在arch/x86/mm/physaddr.c中如下:

 unsigned long __phys_addr(unsigned long x)
 {
         unsigned long y = x - __START_KERNEL_map;
 
         /* use the carry flag to determine if x was < __START_KERNEL_map */
         if (unlikely(x > y)) {
                 x = y + phys_base;
 
                 VIRTUAL_BUG_ON(y >= KERNEL_IMAGE_SIZE);
         } else {
                 x = y + (__START_KERNEL_map - PAGE_OFFSET);
 
                 /* carry flag will be set if starting x was >= PAGE_OFFSET */
                 VIRTUAL_BUG_ON((x > y) || !phys_addr_valid(x));
         }
 
         return x;
 }
__START_KERNEL_map 也是一个偏移量,也在arch/x86/include/asm/page_64_types.h中

#define __START_KERNEL_map      _AC(0xffffffff80000000, UL)

其实如果只看 else 部分,简单算术就知道,从虚拟地址到物理地址转换就是减去了一个 PAGE_OFFSET。至于 if 部分,"unlikely" 都告诉你了,这部分代码执行概率不高,哈哈,不去细究 phys_base 是什么了。。。


不过以上简单的转换只适用于内核地址空间,用户地址空间当然就不会这么简单了,现在还没搞懂。。。


已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页