透过内存读写看计算机体系结构(一)

很早之前,在微博看到一条微博,mov %rax, [0x12345678]会发生啥,当时也没细想,回头仔细琢磨了下,发现东西还是不少的。试试展开写点吧,关于这块。不过首先声明,我体系结构也是半吊子,有问题的话,欢迎指正。其实呢,这个问题用通俗的话说就是,读写内存过程中,到底会发生哪些事。

对于读和写的场景,在特定场合,还是有点区别的,我们从读开始吧.也就是说,mov %rax, (%rax)会发生啥呢?

先简单点,只从操作系统角度来说
有以下这段代码

首先,当访问一段非法内存,可能出现segment fault的,也就是大名鼎鼎的段错误…顺带好了,引出第一个小问题,怎么定义一个非法内存?

在现代操作系统中,应用层访问的内存都是虚拟内存,关于虚拟内存的概念,这里不做太过于详细的阐述简单点说吧,操作系统配合cpu的mmu机制,会做下面2个关系的转换

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),这里也就不展开阐述了

  做完这件事以后, 继续做一些剩余的清理工作,把控制权交还给应用层,同时把状态回退到执行读内存操作之前,让应用层重新操作读内存

  • 应用没有成功申请过这段内,那么这个操作是一个非法操作,会抛出一个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层面,上述的处理过程中,又有哪些更加细节的处理

发表评论

电子邮件地址不会被公开。