当前位置: 首页 > 科技观察

Linux虚拟地址空间和物理地址空间是如何映射的?

时间:2023-03-12 02:29:34 科技观察

在之前的文章《CPU是如何访问内存的?》中,我们知道了CPU是如何访问内存的。在这篇文章中,我们将讨论虚拟地址空间和物理地址空间之间的映射。通常32位Linux内核地址空间分为0~3G为用户空间,3~4G为内核空间。注意这里是32位内核地址空间划分,和64位内核地址空间划分不同。我们以X86为例。物理地址空间布局物理地址空间顶部以下的空间被PCI设备的I/O内存映射占用。它们的大小和布局由PCI规范决定。BIOS和VGA适配器占用了640K~1M的地址空间。Linux系统初始化时,会根据实际物理内存的大小,为每个物理页创建一个页对象,所有页对象组成一个mem_map数组。更进一步,Linux内核为了不同的用途,将所有的物理页划分为三种内存管理区,如图所示,分别是:ZONE_DMA:范围为0~16M,该区域的物理页专门用于I/O设备使用的DMA。之所以需要单独管理DMA的物理页,是因为DMA使用物理地址访问内存,不经过MMU,需要连续的缓冲区。因此,为了提供物理上连续的缓冲区,必须专门划分一段物理地址空间。对于DMA。ZONE_NORMAL:范围为16M~896M,内核可以直接使用该区域的物理页。ZONE_HIGHMEM:范围为896M~end,该区域为高端内存,内核不能直接使用。linux虚拟地址内核空间分布在内核映像下,有16M内核空间用于DMA操作。内核空间高端的128M地址主要由3部分组成,分别是vmalloc区、持久内核映射区、临时内核映射区。由于ZONE_NORMAL与内核线性空间存在直接映射关系,内核会将内核代码、GDT、IDT、PGD、mem_map数组等经常使用的数据放在ZONE_NORMAL中。将用户数据、页表(PT)等不常用的数据放在ZONE_HIGHMEM中,只有在访问这些数据时才建立映射关系(kmap())。例如,当内核要访问I/O设备存储空间时,使用ioremap()将物理地址高端的mmio区域内存映射到内核空间的vmalloc区域,并断开使用后的映射关系。Linux虚拟地址用户空间分布用户进程的代码区一般从虚拟地址空间中的0x08048000开始,这是为了方便检查空指针。代码区上面是数据区、未初始化数据区、堆区、栈区,还有参数和全局环境变量。linux虚拟地址与物理地址映射关系Linux将4G线性地址空间分为两部分,0~3G为用户空间,3G~4G为内核空间。由于开启了分页机制,内核要想访问物理地址空间,必须先建立映射关系,然后再通过虚拟地址访问。为了能够访问所有的物理地址空间,需要将所有的物理地址空间都映射到1G的内核线性空间,这显然是不可能的。因此内核将0~896M的物理地址空间一一映射到自己的线性地址空间,以便随时访问ZONE_DMA和ZONE_NORMAL中的物理页;此时内核剩余的128M线性地址空间不足以完全映射所有ZONE_HIGHMEM,Linux采用动态映射的方式,即将ZONE_HIGHMEM中的物理页映射到内核最大128M线性地址空间空间按需分配,其他物理页映射使用后释放映射关系。这种方式虽然存在效率问题,但毕竟内核可以正常访问所有的物理地址空间。