无论在哪种体系结构中,地址空间的访问都是CPU运行的基本,CPU的每一条指令的地址,都必须在CPU所能访问的地址空间内,当然这个空间指的是虚拟地址空间。每个CPU所能访问多大的虚拟地址这是和CPU的位数有关的,32位地址总线,最大的虚拟地址就是2的32次方,64位同理。当然在实际的使用中,是不会出现这么大的地址范围的,我们都会根据设备的地址和内存的范围来配置一些窗口来给CPU访问,这就限制了CPU实际所使用的地址空间的大小。这些虚拟地址空间又是如何映射到实际的物理地址(设备地址和内存地址)的,每种结构的映射方法可能不同。不过大同小异,只要掌握一种其他的都不难理解。
简单的讲CPU拿到的地址都是虚拟地址,CPU区访问地址的时候,有的地址是落在了内存上,而有的地址落在了设备的寄存器上,这是怎么实现的呢,主要是通过窗口的配置实现的。内核通过MMU的分段管理和分页管理机制来管理内核的内存映射。
CPU拿到虚拟地址后经过MMU的分段管理得到了线性地址,线性地址又经过分页管理得到了物理地址,逻辑地址基于分段管理或者分页管理中基于基址的偏移值。(分段管理和分页管理这里不做详细介绍)进程使用的虚拟地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。虚拟地址通过页表(Page Table)映射到物理内存,页表由操作系统维护并被处理器引用。内核空间在页表中拥有较高特权级,因此用户态程序试图访问这些页时会导致一个页错误(page fault)。在Linux中,内核空间是持续存在的,并且在所有进程共享。内核代码和数据总是可寻址,随时准备处理中断和系统调用。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化。
(1)ARM地址空间
32ARM结构地址空间划分:
地址分布如下图所示:
如上图所示:
用户空间(0—3G),这段空间映射到物理内存的高端内存
内核空间(3G—4G),这段空间映射到低端内存,这段空间又分为以下四部分,分别为
(a)直接映射区(0—896M):这段虚拟地址空间和低端内存地址存在线性的地址关系即虚拟地址3G+X=物理地址X
(b)动态映射区(896—1016M):这段 空间具体映射到物理内存的什么位置不确定,该区域的地址由内核中的vmalloc来实现 分配,其特点是虚拟地址空间连续,但是物理地址空间不一定连续。vmalloc函数返回的是虚拟地址,但是其映射的物理地址有可能在高端内存,也有可能在低端内存。
(c)永久内存映射区(pkmap1016—1020M):使用kmap函数将高端内存的地址映射到这部分区域,这样就可以通过这个 虚拟地址来访问高端内存的地址。通过这4M的窗口可以重复映射所有的高端内存。
(d)固定映射区:(1020—1024M):这4M的地址是有特定用途的固定地址,这4M的区域映射的物理内存作为ACPI电源管理等寄存器的地址。
64位ARM地址空间的划分:
ARM64架构处理器采用48位物理寻址,最大可以支持256T的地址空间,对于目前的应用来说已经足够了。但是虚拟地址依然采用64,虚拟地址远远大于物理地址。所以在处理器架构设计上,把虚拟地址空间划分为三部分,分别为用户空间,非规范区和内核空间,其中内核空间和用户空间每个部分最大支持256T的访问。
用户空间:(0x0000_0000_0000_0000——0x0000_FFFF_FFFF_FFFF)256T
内核空间:(0xFFFF_0000_0000_0000——0xFFFF_FFFF_FFFF_FFFF)256T
其余部分被称为非规范区域。
内核空间由做了详细的划分,分为以下部分:
(a)Vmalloc区域:0xFFFF_0000_0000_0000——0xFFFF_7BFF_BFFF_0000(126974G)
(b)Vmemmap区域:0xFFFF_7BFF_C000_0000——0xFFFF_7FFF_C000_0000(4096G)
(c)PCI I/O区域:0xFFFF_7FFF_AE00_0000——0xFFFF_7FFF_BE00_0000(16M)
(d)Moudules区域:0xFFFF_7FFF_C000_0000——0xFFFF_8000_0000_0000(64M)
(e)Normal memory线性映射区:0xFFFF_8000_0000_0000——0xFFFF_FFFF_FFFF_FFFF(128T)
如下图所示:
(2)MIPS结构的地址空间
32位MIPS地址空间划分(loongson平台):
如下图所示:
如上图所示:在mips结构上,32位虚拟地址空间也同样分为两部分,即用户空间和内核空间。只不过哦与ARM结构不同,这里的内核空间是2G大小。
(a)内核空间(0x8000_0000—0xFFFF_FFFF)2G
(b)用户空间(0x0000_0000——0x7FFF_FFFF)2G
内核空间又做了比较详细的划分,分别为kseg0,kseg1,kseg2。
kseg0:(0x8000_0000——0x9FFF_FFFF)512M
这部分虚拟地址被映射到物理内存的0到512M空间内,访问这个窗口的地址是需要cache的,也就意味着CPU来这个窗口取内存的数据的时候,会将数据添加到cache中,下次再取的时候就可以直接从cache中获取。
kseg1:(0xA000_0000——0xBFFF_FFFF)512M
这部分虚拟地址也被映射到物理内存的0到512M空间内,和上面属于重复映射,读取的数据属于内存的同一块内存,只不过这部分地址不经过cache,也就是CPU用这个窗口的指令去内存中取数据的时候是不经过cache的,cache中没有数据的拷贝。每次取都要从内存中读取。
kseg2:(0xC000_0000——0xFFFF_FFFF)1G
这部分虚拟地址被映射到物理内存的0x4000_000到0x7FFF_FFFF这1G的内存地址空间,作为PCI的memory空间。这个映射是TLB完成的。
用户空间虚拟地址0x0000_0000——0x7FFF_FFFF经过TLB被映射到物理内存的0x8000_0000向上的物理内存,这里面需要扣掉
0x8000_0000——0x9000_0000这256M的内存,因为这部分地址被reserved掉了不是用。所以用户空间真正的物理内存是0x9000_0000向上的地址空间。
64位MIPS地址空间划分(loongson平台)
64位地址空间划分的比较复杂,和ARM一样虚拟地址还是采用64位地址,物理地址采用了48位的物理地址,但是48位中高4位为节点号,其余的44位才是每个设备内的物理地址分布。
节点空间分布如下图所示:
而每个节点内的地址空间分布如下图所示:
如上图所示,内核模式下,虚拟地址被划分为以下五部分:
(a)用户空间:0x0000_0000_0000_0000——0x0001_0000_0000_0000(256T)
(b)地址空洞:如上图中紫色部分,内核模式下访问会出错的地址
(c)可扩展区:0x4000_0000_0000_0000——0x4000_FFFF_FFFF_FFFF和
0xC000_0000_0000_0000——0xC000_00FF_7FFF_FFFF(257T+2G)
(d)xkphys段:0x8000_0000_0000_0000——0xBFFF_FFFF_FFFF_FFFF(4E)
(e)完全复用32位地址的部分:0xFFFF_FFFF_8000_0000——0xFFFF_FFFF_FFFF_FFFF(4G)
如何用64位地址来访问48位的物理地址呢?请看下图:
我们在软件代码上,要用64位的虚拟地址来访问设备,只需要牢记,用0x9800_xxxx_xxxx_xxxx和0x9000_xxxx_xxxx_xxxx的地址就可以访问全部48位地址空间,用0x98开头的地址是经过cache的,0x90开头的地址是不经过cache的,其中低32位地址完全复用32位模式下的地址即可。
本文链接:https://www.kinber.cn/post/627.html 转载需授权!
推荐本站淘宝优惠价购买喜欢的宝贝: