linux内核虚拟地址空间

Linux内核地址空间划分


通常 32 位 Linux 内核地址空间划分 0~3G 为用户空间,3~4G 为内核空间 。64 位内核地址空间划分是不同的 。


linux内核虚拟地址空间

文章插图


Linux内核高端内存


当内核模块代码或线程访问内存时,代码中的内存地址都为逻辑地址,而对应到真正的物理内存地址,需要地址一对一的映射,如逻辑地址 0xc0000003 对应的物理地址为 0x3,0xc0000004 对应的物理地址为 0x4,… …,逻辑地址与物理地址对应的关系为


物理地址 = 逻辑地址 – 0xC0000000


逻辑地址
物理内存地址
0xc0000000
0x0
0xc0000001
0x1
0xc0000002
0x2
0xc0000003
0x3


0xe0000000
0x20000000


0xffffffff
0x40000000


假设按照上述简单的地址映射关系,那么内核逻辑地址空间访问为0xc0000000 ~ 0xffffffff,那么对应的物理内存范围就为0x0 ~ 0x40000000,即只能访问 1G 物理内存 。若机器中安装 8G 物理内存,那么内核就只能访问前 1G 物理内存,后面 7G 物理内存将会无法访问,因为内核的地址空间已经全部映射到物理内存地址范围 0x0 ~ 0x40000000 。即使安装了 8G 物理内存,那么物理地址为 0x40000001 的内存,内核该怎么去访问呢?代码中必须要有内存逻辑地址的,0xc0000000 ~ 0xffffffff 的地址空间已经被用完了,所以无法访问物理地址 0x40000000 以后的内存 。


显然不能将内核地址空间 0xc0000000 ~ 0xfffffff 全部用来简单的地址映射 。


因此,Linux 又把物理页面划分为3 个区:
  • 专供 DMA 使用的 ZONE_DMA 区(小于 16MB);
  • 常规的 ZONE_NORMAL 区(大于 16MB 小于 896MB);
  • 内核不能直接映射的区 ZONE_HIGME 区(大于 896MB) 。


以上每个区都用 struct zone_struct 结构来表示 。


ZONE_HIGHMEM 即为高端内存,这就是内存高端内存概念的由来 。


linux内核虚拟地址空间

文章插图


其中把 0~896M 区域为直接映射区,也即是虚拟内存中(3G~3G+896M)区域和物理内存的 0~896M 进行直接映射 。由于虚拟内存中内核空间只有1G,因此还剩下的 128M 虚拟内存区域(3G+896M~4G) 。


那么如内核是如何借助 128MB 高端内存地址空间是如何实现访问可以所有物理内存?


当内核想访问高于 896MB 物理地址内存时,从 0xF8000000 ~ 0xFFFFFFFF 地址空间范围内找一段相应大小空闲的逻辑地址空间,借用一会 。借用这段逻辑地址空间,建立映射到想访问的那段物理内存(即填充内核 PTE 页面表),临时用一会,用完后归还 。这样别人也可以借用这段地址空间访问其他物理内存,实现了使用有限的地址空间,访问所有所有物理内存 。如下图 。


linux内核虚拟地址空间

文章插图


例如内核想访问 2G 开始的一段大小为1MB的物理内存,即物理地址范围为0x80000000 ~ 0x800FFFFF 。访问之前先找到一段 1MB 大小的空闲地址空间,假设找到的空闲地址空间为 0xF8700000 ~ 0xF87FFFFF,用这 1MB 的逻辑地址空间映射到物理地址空间 0x80000000 ~ 0x800FFFFF 的内存 。映射关系如下:


逻辑地址
物理内存地址
0xF8700000
0x80000000
0xF8700001
0x80000001
0xF8700002
0x80000002


0xF87FFFFF

推荐阅读