2

Linux的进程地址空间[一]

 2 years ago
source link: https://zhuanlan.zhihu.com/p/66794639
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Linux的进程地址空间[一]

talk is cheap

所谓进程地址空间(process address space),就是从进程的视角看到的地址空间,是进程运行时所用到的虚拟地址的集合。

32位系统的进程地址空间

以IA-32处理器为例,其虚拟地址为32位,因此其虚拟地址空间的范围为 [公式] ,Linux系统将地址空间按3:1比例划分,其中用户空间(user space)占3GB,内核空间(kernel space)占1GB。

假设物理内存也是4GB(事实上,虚拟地址空间的范围不一定需要和物理地址空间的大小相同),则虚拟地址空间和物理地址空间的转换如下图所示:

v2-903ce9a2c1d5203183f78ad56a41eb66_720w.jpg

因为内核的虚拟地址空间只有1GB,但它需要访问整个4GB的物理空间,因此从物理地址0~896MB的部分(ZONE_DMA+ZONE_NORMAL),直接加上3GB的偏移(在Linux中用PAGE_OFFSET表示),就得到了对应的虚拟地址,这种映射方式被称为线性/直接映射(Direct Map)。

而896M~4GB的物理地址部分(ZONE_HIGHMEM)需要映射到(3G+896M)~4GB这128MB的虚拟地址空间,显然也按线性映射是不行的。

采用的是做法是,ZONE_HIGHMEM中的某段物理内存和这128M中的某段虚拟空间建立映射,完成所需操作后,需要断开与这部分虚拟空间的映射关系,以便ZONE_HIGHMEM中其他的物理内存可以继续往这个区域映射,即动态映射的方式。

用户空间的进程只能访问整个虚拟地址空间的0~3GB部分,不能直接访问3G~4GB的内核空间部分,但出于对性能方面的考虑,Linux中内核使用的地址也是映射到进程地址空间的(被所有进程共享),因此进程的虚拟地址空间可视为整个4GB(虽然实际只有3GB)。

v2-70675b90a312cb1f55271522442ad41a_720w.jpg

64位系统的进程地址空间

在64位系统中,进程地址空间的大小就不固定了,以ARMv8-A为例,它的page大小可以是4KB, 16KB或者64KB(默认为4KB,选一种来用,不要混用),可采用3级页表或4级页表,因此可以有多种组合的形式。

以采用4KB的页,4级页表,虚拟地址为48位的系统为例(从ARMv8.2架构开始,支持虚拟地址和物理地址的大小最多为52位),其虚拟地址空间的范围为 [公式] ,按照1:1的比例划分,内核空间和用户空间各占128TB。

256TB已经很大很大了,但是面对64位系统所具备的16EB的地址范围,根本就用不完。为了以后扩展的需要(比如虚拟地址扩大到56位),用户虚拟空间和内核虚拟空间不再是挨着的,但同32位系统一样,还是一个占据底部,一个占据顶部,所以这时user space和kernel space之间偌大的区域就空出来了。

但这段空闲区域也不是一点用都没有,它可以辅助进行地址有效性的检测。如果某个虚拟地址落在这段空闲区域,那就是既不在user space,也不在kernel space,肯定是非法访问了。使用48位虚拟地址,则kernel space的高16位都为1,如果一个试图访问kernel space的虚拟地址的高16位不全为1,则可以判断这个访问也是非法的。同理,user space的高16位都为0。这种高位空闲地址被称为canonical。

在64位系统中,内核空间的映射变的简单了,因为这时内核的虚拟地址空间已经足够大了,即便它要访问所有的物理内存,直接映射就是,不再需要ZONE_HIGHMEM那种动态映射机制了。

64位系统中用户空间的映射和32位系统没有太大的差别。

ARM公司宣称64位的ARMv8是兼容32位的ARM应用的,所有的32位应用都可以不经修改就在ARMv8上运行。那32位应用的虚拟地址在64位内核上是怎么分布的呢?事实上,64位内核上的所有进程都是一个64位进程。要运行32位的应用程序, Linux内核仍然从64位init进程创建一个进程, 但将用户地址空间限制为4GB。通过这种方式, 我们可以让64位Linux内核同时支持32位和64位应用程序。

要注意的是, 32位应用程序仍然对应128TB的内核虚拟地址空间, 并且不与内核共享自己的4GB虚拟地址空间, 此时用户应用程序具有完整的4GB虚拟地址。而32位内核上的32位应用程序只有3GB真正意义上的虚拟地址空间。

那进程地址空间到底是由哪些元素构成的呢?请看下文分解。

参考:

http://jake.dothome.co.kr/pt64/,里面对ARM64的各种page size和页表级数的组合做了详尽介绍。

https://www.kernel.org/doc/Documentation/arm64/memory.txt

http://thinkiii.blogspot.com/2014/02/arm64-linux-kernel-virtual-address-space.html, 这是本文后半部分的图片来源和主要参考。

原创文章,转载请注明出处。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK