最近在看Linux Kernel Exploit相关的东西,要用到一些栈和地址相关的知识,所以就看一点补充一点吧。这篇文章介绍一下关于物理地址和虚拟地址的相关知识。
物理和虚拟寻址
计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组。每字节都有一个唯一的物理地址(Physical Address,PA)
。CPU访问存储器的最自然的方式就是使用物理地址。这种方式称为物理寻址(physical addressing)
。
早期的PC使用物理寻址,现代处理器使用的是一种称为虚拟寻址(virtual addressing)
的寻址方式。CPU通过生成一个虚拟地址(Virtual Address,VA)
来访问主存,这个虚拟地址在被送到存储器之前转换成适当的物理地址。将一个虚拟地址转换为物理地址的任务叫做地址翻译(address translation)
。CPU上叫做存储器管理单元(Memory Management Unit,MMU)
的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容是由操作系统管理的。
地址空间
地址空间(address space)
是一个非负整数地址的有序集合:{0, 1, 2, …}
如果地址空间中的整数是连续的,那么就说它是一个线性地址空间(linear address space)
。在一个带虚拟存储器的系统中,CPU从一个有N=$2^n$个地址的地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间(virtual address space)
:{0, 1, 2, …, N-1}
一个地址空间的大小是由n
来描述,叫做一个n
位地址空间。现代系统典型支持32位或64位虚拟地址空间。
一个系统还有一个物理地址空间(physical address space)
,它与系统中物理存储器的M个字节相对应:{0, 1, 2, …, M-1}
地址空间清楚地区分了数据对象(字节)和它们的属性(地址)。每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间。这就是虚拟存储器的基本思想。主存中的每个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。
物理地址空间布局
如上图所示,在物理地址空间中,内存划分如下:
内存区域 | 地址空间 | 说明 |
---|---|---|
ZONE_DMA | 0~16M | 该区域的物理页面专门供I/O设备的DMA使用 |
ZONE_NORMAL | 16M~896M | 该区域的物理页面是内核能够直接使用的 |
ZONE_HIGHMEM | 896M~4G | 该区域即为高端内存,内核不能直接使用 |
虚拟地址空间布局
如上图,32位Linux中虚拟地址空间划分0~3G
为用户空间,3~4G
为内核空间(64位的划分是不同的)。其中内核空间中,在kernel image下面有16M的内核空间用于DMA操作。位于内核空间高端的128M地址主要由3部分组成,分别为vmalloc area,持久化内核映射区,临时内核映射区。
当内核模块代码或线程访问内存时,代码中的内存地址都为虚拟地址,需要映射为物理地址。
虚拟地址与物理地址映射关系
如图所示,虚拟地址空间中的3G ~ 3G+896M
(0xC0000000 ~ 0xF8000000)和物理地址空间的0 ~ 896M
(0 ~ 0x38000000)一一映射。当内核想访问高于896MB物理地址内存时,从虚拟地址空间3G+896M ~ 4G
(0xF8000000 ~ 0xFFFFFFFF)范围内找一段相应大小空闲的地址空间,借用这段地址空间,建立映射到想访问的那段物理内存(即填充内核PTE页面表),临时用一会,用完后归还。这样别人也可以借用这段地址空间访问其他物理地址空间,实现了使用有限的地址空间,访问所有所有物理地址空间。
例如,内核想访问2G开始的一段大小为1MB的物理内存,即物理地址范围为0×80000000 ~ 0x800FFFFF。访问之前先找到一段1MB大小的空闲虚拟地址空间,假设找到的空闲地址空间为0xF8700000 ~ 0xF87FFFFF,用这1MB的虚拟地址空间映射到物理地址空间0×80000000 ~ 0x800FFFFF的内存。
用户空间布局
用户进程没有高端内存概念。只有在内核空间才存在高端内存。用户进程最多只可以访问3G物理内存,而内核进程可以访问所有物理内存。
reference
深入理解计算机系统
http://blog.chinaunix.net/uid-15007890-id-3415331.html
http://www.cnblogs.com/chengxuyuancc/archive/2013/04/17/3026920.html