Linux内存管理

基础概念关系


在计算机架构中,硬件主板上主体分为南北桥,北桥主要是CPU内存显卡等之间的数据传输,而南桥主要负责的是IO相关的、外部存储设备、BIOS相关的数据。

而我们所知的内存管理,实际是程序的逻辑地址,通过分段机制转为线性地址,通过分页机制转为物理地址(这一部由CPU的MMU模块支持),物理地址再通过北桥访问到具体的RAM设备,或者是ISA设备,或者PCI等IO相关(再通过南桥)。

内存寻址的工作是由Linux内核和MMU共同完成的,其中Linux内核负责cr3,gdtr等寄存器的设置,页表的维护,页面的管理,MMU则进行具体的映射工作。

另外,学过汇编语言我们知道最原始的访问物理地址是通过CS:IP的分段机制来访问的, 所以x86架构由于历史原因是是分段机制,但是Linux的内存管理是分页机制。为了向前兼容我们把整个内存段落拓宽为只有一段,段基址为0,段限长为4G,只是在段类型和段访问权限上有所区分,并且Linux内核和所有进程共享1个GDT,不使用LDT(即系统中所有的段描述符都保存在同一个GDT中),这是为了应付CPU的分段机制所能做的最少工作。 

具体线性地址和物理地址转换可以参考如图

 

 

物理地址空间


 

0-16M ZONE_DMA,该区域的物理页面专门供I/O设备的DMA使用。之所以需要单独管理DMA的物理页面,是因为DMA使用物理地址访问内存,不经过MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于DMA。
16M-896M

ZONE_NORMAL,该区域的物理页面是内核直接映射,即内核空间的常用的数据如kernel代码、GDT、IDT、PGD、mem_map数组等放在ZONE_NORMAL里,为的是内核该虚拟地址与物理地址是直接映射,不通过页机制。

896M-4G ZONE_HEIGHT,该区域是物理高端地址,映射到用户空间和内核部分空间。
640k-1M 这段地址空间被BIOS和VGA适配器所占据。RAM地址空洞。
MMIO

在MMIO中,IO设备和内存共享同一个地址总线,它们的地址空间是相同的; 而在PMIO中,IO设备和内存的地址空间是隔离的。简单点说就是MMIO将IO地址编入内存地址中,调用相应的内存地址即调用到相应的IO设备,而至于往下的是像Inter的int/out指令端口独立编址还是ARM的统一编址则不管。

 

虚拟地址空间


 

 

虚拟地址空间 实际意义
0-3G 用户空间
3G-4G 内核空间
3G-3G+16M 对应的物理地址中的ZONE_DMA
3G+16M-3G+896M 对应的物理地址中的ZONE_NORMAL
VMALLOC_START-VMALLOCEND vmalloc area
 PKMAP_BASE 持久化内核映射区
 FIXADDR_BASE 临时内核映射区

 

 

内核空间


 

对于虚拟地址空间,我们知道0-3G是用户空间,而3G-4G是内核空间,前896M已经用于直接映射了,那么剩下的128M空间需要映射其他的不同位置物理空间,即高端物理空间。

Kernel提供了三种机制,分别是vmalloc area,持久化内核映射区,临时内核映射区。

  • vmallo area,提供连续的虚拟地址,但是指向的物理地址不一定连续
  • 持久化内核映射区,方便通过物理地址找到对应的虚拟地址,用于固定的线性地址常量,例如0xffffc000,且该常量在编译阶段就可以确定。创建持久内核映射的函数kmap()可能会阻塞当前进程,因此不能用在中断上下文中。
  • 临时内核映射区,与持久内核映射相比,它更快,而且不会阻塞当前进程,因此可以用在中断上下文中。不过,它也有一个弱点,就是使用它的代码不能睡眠。因为每一个CPU都有独自的13个临时映射地址如果切换进程可能导致该地址被改变。

 

用户空间 


  

 

用户进程的代码区一般从虚拟地址空间的0x08048000开始,这是为了便于检查空指针。代码区之上便是数据区,未初始化数据区,堆区,栈区,以及参数、全局环境变量。

 

 

 

参考:

Linux内存寻址和内存管理 //进程

内存管理 //进程

Linux内存管理

linux内核之内存管理图解 //进程

Kernel那些事儿之内存管理(11) --- 内核映射 

浅谈内存映射I/O(MMIO)与端口映射I/O(PMIO)的区别

posted @ 2018-08-19 01:01  Grim_Reaper  阅读(286)  评论(0编辑  收藏  举报