计算机结构

为了更好地理解OS,这里将计算机组成放到这里。
"计算机结构"

CPU有不同字长区分,CPU字长指整数和指针数据的标称大小。早期PC机性能较差,多为32位计算机。现多为64位计算机。
通过指针字节长度可以算出虚拟地址最大存储空间(即指针排列情况),32位计算机只有2^32存储空间,64位计算机有2^64的存储空间。
但只是虚拟地址,实际物理地址可能并没有这么大空间。

应用程序也不会直接调用硬件级别的命令,而是使用OS级别的命令,通过OS调用硬件命令。
这是为了实现OS系统的特性:

  • isolation 隔离性:将软件和物理内存分分隔,保证进程Crush不会导致整个OS崩溃。
  • multiplexing 多进程同时操作:将软件和物理内存分分隔,保证进程间互不影响。
  • interaction 交互性:提供统一的硬件调用方法。

页表

通过页表实现了虚拟地址到物理地址的映射。
"虚拟地址"
在虚拟地址中,堆从下往上增长,栈从上往下增长。
.text是代码区,放的是编译好的代码。
data区域是全局变量和静态变量。

虚拟地址还需要在CPU中翻译为物理地址。负责计算物理地址的叫memory management unit(mmu)。
在RISC-V操作系统中,地址后12位为offset,前面为物理页表的页号。页表中记录PPN(Physical Page Number)和flag(记录地址状态,如valid等)
"虚拟地址To物理地址"

分级页表

但前面提到虚拟地址可能要比物理地址大(可以等于,也可以小于,但一般VA大于PA),是因为在VA映射到PA时PPN可能是无效的,即没有实际对应的物理内存,就会及时开辟一个新的页表让该地址对应到物理地址。而如图一张页表,字节数也较大,为了一个地址申请一个新表可能会很耗时,遂考虑解决方法。
考虑将页表分级。

"分级页表"

快表TLB

实际上mmu映射到PA也是个复杂的过程,为了快速访问,会将最近使用的VA和PA映射关系存到一个缓存表中,名为快表TLB

  • 总结:因此,软件进程中访问一个虚拟地址,流程是:查TLB —》无结果则mmu映射到物理地址 —》多级缓存机制加速地址访问过程…

Calling Convention & Stack Frames

  • Review
    C/C++代码需要编译为二进制代码。
    编译过程如下:
    "编译过程"

Stack Frames && Calling Convention

编译后的代码会存储在.text代码段,调用函数即使用函数第一行的地址,如下:
"FunctionCall"

函数嵌套调用时会先移动sp指针,包含调用函数中创建的临时变量、前一帧的地址、函数返回地址等, 调用结束后返回fp指针(返回地址),sp向后移动。
sp指向栈“顶”,fp指向当前的栈帧首地址。
"StackFrame"

寄存器对编译的函数有统一的限制,如寄存器a0~a1仅用于存储函数参数或返回值。这里不做深入讨论。
仅补上下面概念:
Caller —— 返回一个调用当前函数的引用
Callee —— 返回一个正在被执行函数的引用

trap

在调用ecall时(如fork()、write()等),需要权限从用户空间转换到内核空间,其中的设计和细节对建立OS系统的隔离性和安全性很重要,。

什么是trap

trap 是 CPU 提供的一种不可绕过的控制转移机制。当程序在运行过程中发生以下情况之一时,CPU 会强制中断当前执行流,将控制权交给操作系统内核:

  • System Call:程序主动请求内核服务(如 ecall)
  • Exception:程序执行过程中出现异常(如 page fault、非法指令)
  • Interrupt:外部或定时事件(如时钟中断、设备中断)
    trap主要负责,在不信任当前程序的前提下,将 CPU 的控制权安全地移交给内核。

比如,ecall时需要从user space到kernel space再切换回user space,该过程就是trap。
"Trap"

trap的过程

一个程序运行最后都是在寄存器上执行汇编代码的。主要有一定数量的寄存器(a0、a1等与软件执行相关的寄存器)和特殊寄存器(satp、satt、stvec、sscratch等)。为了隔离和安全,且让任何程序都能在os上互不干扰地执行,就要在ecall时保存进入内核空间的程序堆栈,还不能保存到程序堆栈上。

(略过了一万个细节…暂时不想在这上面浪费很多时间)

另一点是,user page始终会保存一个用来缓存寄存器(仅a0、a1那些)的页表。系统启动时和每次回到user space时都会重新设置user page的该值以便下次ecall时使用。

Page Faults

总结 Conclusion