透过内存读写看计算机体系结构(一)
很早之前,在微博看到一条微博,mov %rax, [0x12345678]会发生啥,当时也没细想,回头仔细琢磨了下,发现东西还是不少的。试试展开写点吧,关于这块。不过首先声明,我体系结构也是半吊子,有问题的话,欢迎指正。其实呢,这个问题用通俗的话说就是,读写内存过程中,到底会发生哪些事。
对于读和写的场景,在特定场合,还是有点区别的,我们从读开始吧.也就是说,mov %rax, (%rax)会发生啥呢?
先简单点,只从操作系统角度来说
有以下这段代码
1 2 3 |
int *pvalue = get_val(); int value = *pvalue; |
首先,当访问一段非法内存,可能出现segment fault的,也就是大名鼎鼎的段错误…顺带好了,引出第一个小问题,怎么定义一个非法内存?
在现代操作系统中,应用层访问的内存都是虚拟内存,关于虚拟内存的概念,这里不做太过于详细的阐述简单点说吧,操作系统配合cpu的mmu机制,会做下面2个关系的转换
1 2 3 |
f(phy_mem_addr) = virt_mem_addr f'(virt_mem_addr) = phy_mem_addr |
cpu在实际访问内存的时候,要从虚拟地址翻译成物理地址,当cpu访问的一段虚拟地址,没有办法找到对应的物理地址的时候,那么cpu会抛出一个异常(page fault的来源),然后,操作系统会处理这个page fault,进行一系列的异常处理。
在操作系统层面看来,如果一段虚拟地址空间[start, end),内核已经分配给了应用层,那么任何一段内存只要只要落在[start, end)这个区间内部都叫做合法内存,同时,内核会记录这块虚拟地址空间是有效的。
反之,称之为非法内存
实际的场景下,很多内存的申请,可能应用并不会立即使用,或者说,并不会使用到全部的内存为了避免物理内存浪费的场景,因此,内核尽可能会推迟给应用分配真正物理内存的时机,那么最简单的处理手段就是,在应用真的对内存发生了读写操作的时候,再分配但是,这样一来,就会导致一个问题,即使对于一段应用有权限的内存,也可能出现没有对应物理内存的情况,那么,cpu会抛出page fault,操作系统会通过中断向量接到这个异常,当他发现发生中断的进程访问的这段内存地址.
-
应用已经成功申请过的内存的.他会认为这是一个合法的行为,会进行一些尝试恢复异常的处理内存是有权限的,比如可读,可写,可执行,这些权限位,同样是操作系统维护的,他们维护在内存页面的page层面(操作系统最小的内存管理单位是page)
- 这段内存对应的page,没有读权限,抛出segment fault
-
这段内存对应的page,有读权限
- 这段内存之前被swap出去了
从物理内存中分配一个new_page, 这个原来这个page在swap中对应的内容content, copy(content, new_page) - 这段内存之前没有被访问过
从物理内存中分配一个new_page,如果这个时候申请不出物理内存了,会触发一系列内核的内存回收工作,可能导致OOMK(out of memory kill),这里也就不展开阐述了
- 这段内存之前被swap出去了
做完这件事以后, 继续做一些剩余的清理工作,把控制权交还给应用层,同时把状态回退到执行读内存操作之前,让应用层重新操作读内存
- 应用没有成功申请过这段内,那么这个操作是一个非法操作,会抛出一个segment fault
顺带,这里引出几个问题
- 如何申请一段保证有物理内存映射的虚拟内存?
- 哪些内存会被置换到swap中去
- 什么时候会触发swap的行为
然后,继续复杂化场景,在操作系统外边再套一层hypervisor,又是如何一个处理流程呢?
这里就涉及到不少vt相关的东西,其实当发生page fault的时候, 对于x86的cpu来说,会触发一个vmexit事件,hypervisor会接管控制权.hypervisor在处理他自己的物理内存和vm内存的转换关系(对于hypervisor来说,vm就是一个普通的进程),把物理页面映射好,然后把控制权还给vm,然后重复走上述的流程,也就是说实际的处理流程变成了:
application in vm page fault -> hypervisor page fault hanlder -> os in vm page fault hanlder -> application in vm running
下一篇,我们开始讲,在cpu
层面,上述的处理过程中,又有哪些更加细节的处理
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
