概述
一、布局图
二、说明
名称 | 存储内容 |
stack | 局部变量、函数参数、返回地址等。 |
heap | 动态分配的内存。 |
bss | 未初始化 或 初值为0 的全局变量和静态局部变量。 |
data | 已初始化 且 初值非0 的全局变量和静态局部变量。 |
text | 可执行代码、字符串字面值、只读变量。 |
reserved | 不可访问,用于捕捉使用空指针和小整型值指针引用内存的异常情况。 |
Random stack offset Random mmap offset Random brk offset | 意在防止恶意程序。 Linux通过对栈、内存映射段、堆的起始地址加上随机偏移量来打乱布局,以免恶意程序通过计算访问栈、库函数等地址。 |
mmap | 磁盘上的文件映射到虚拟地址空间中,用于装载动态共享库。 |
三、拓展
1、分段的好处。
- 进程运行过程中,代码指令根据流程依次执行,只需访问一次(当然跳转和递归可能使代码执行多次);而数据(数据段和BSS段)通常需要访问多次,因此单独开辟空间以方便访问和节约空间。
- 当程序被装载后,数据和指令分别映射到两个虚存区域。数据区对于进程而言可读写,而指令区对于进程只读。两区的权限可分别设置为可读写和只读。以防止程序指令被有意或无意地改写。
- 现代CPU具有极为强大的缓存(Cache)体系,程序必须尽量提高缓存命中率。指令区和数据区的分离有利于提高程序的局部性。现代CPU一般数据缓存和指令缓存分离,故程序的指令和数据分开存放有利于提高CPU缓存命中率。
- 当系统中运行多个该程序的副本时,其指令相同,故内存中只须保存一份该程序的指令部分。若系统中运行数百进程,通过共享指令将节省大量空间(尤其对于有动态链接的系统)。其他只读数据如程序里的图标、图片、文本等资源也可共享。而每个副本进程的数据区域不同,它们是进程私有的。
- 临时数据及需要再次使用的代码在运行时放入栈区中,生命周期短。全局数据和静态数据可能在整个程序执行过程中都需要访问,因此单独存储管理。堆区由用户自由分配,以便管理。
四、虚拟地址和物理地址映射原理
由于应用程序使用的内存地址是虚拟地址,为了能够将其与实际的物理内存达成一个映射,就需要一个表将虚拟地址映射到实际的物理内存地址,这个表就叫做页表,是存储在主存(RAM)里的。页表中的元素就叫做页表项,即虚拟地址与物理地址的映射,具体一点,我们直接看图,以区分开页表、页表项、虚拟地址与物理地址:
虚拟地址:也就是应用看到的内存地址,在本文就是一个 4B(32bit)的地址,分为索引值 VPN(Virtual Page Number)和页面偏移量,页面偏移量位数由页大小(page size)决定,Linux 默认页大小为 4KB,我理解这里的页大小也指的是这页地址所指向的总内存大小,也就是说,一页有 4K 个内存地址(映射),即 2^12,这样的话,同一页的内存只有后面 12 位是变化的,前面 20 位是固定的,如图所示;
物理地址:也就是实际的物理内存地址,和虚拟地址的划分一致,分为 PFN(Page Frame Number)和页面偏移量,页面偏移量同样由页大小决定,在默认的 4KB 页大小下,同样是 12 位的偏移量和 20 位的PFN;
所以说,页表其实就是要 VPN 到 PFN 的映射;
页表:页表存储着的其实是 VPN 到 PFN 的映射,是以 array 的形式连续存储的,所以 VPN 作为索引值是隐含在其中的,无需存储在页表中,只需要知道页表的基地址即可,这点类似数组和其下标的关系。系统根据虚拟地址的 VPN 和页表基地址,即可查到对应的 PFN,而虚拟地址的 page offset 就是对应物理地址的 offset,PFN + offset,就得到了虚拟地址对应的物理地址。
页表项:即页表中存储的元素,带有 VPN、PFN 之间的映射关系以及其他信息(是否缺页标志位、保护位、修改位、访问位和高速缓存禁止位…);由于 page size 变化会导致 VPN 和 PFN 的位数发生变化,所以表项大小其实是不确定的,具体情况具体对待;
参考:
linux进程虚拟地址空间 - 前进的code - 博客园
【笔记】图说Linux下的虚拟内存寻址、页表_WuPeng_uin的博客-CSDN博客
(SAW:Game Over!)
最后
以上就是玩命大米为你收集整理的OS / Linux / 进程的虚拟地址空间布局以及与物理地址映射原理的全部内容,希望文章能够帮你解决OS / Linux / 进程的虚拟地址空间布局以及与物理地址映射原理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复