1599 字
8 分钟
Loading
机制:受限直接执行

为了虚拟化cpu,操作系统需要以某种方式让许多任务共享物理cpu,基本思想是这样的:让一个进程运行一段时间,然后运行另一个进程,如此轮换。

通过这种方式时分共享cpu,就实现了虚拟化。

然而,这样的机制存在一些挑战,比如对性能有一定的需求,第二是控制权,如何有效运行进程还能保留对cpu的控制,控制权对于操作系统来说尤为重要。

设计目标:获得一个高性能可控的虚拟化cpu

基本技巧:受限直接执行

为了能使程序尽快的运行,就有了 直接执行 的概念,即在cpu上直接运行程序,将静态代码加载到内存里并运行,这种基本的直接协议没有任何限制。直接执行虽然快速(几乎等同于期望时间),但在cpu上运行依然会带来问题,即如果进程希望执行某种受限操作(例如请求更多的cpu或内存,或是频繁的io)则又会成为新的问题。

一个进程必须能够执行一些受限操作以保证其正常运行,但又不能让它完全控制系统,为了解决这个问题,则引入了一种新的处理器模式,成为用户模式。在这个模式下运行的代码会受到各种限制,进程不能发出io请求,与用户模式不同的是内核模式,操作系统或内核就以这种模式运行,在这个模式下,运行的代码可以做所有事情,包括特权和io请求以及全部的受限指令。

即便如此,还会有很多的不便,例如如果用户希望执行某种特权操作(比如直接从磁盘读取)则会受限,为了实现这一点,几乎所有的硬件都提供了用户程序执行系统调用的能力,它允许内核小心的向用户程序暴露某些关键的功能,要执行系统调用,程序就需要执行特殊的陷阱指令,完成操作后,特权级别降低,重新回到用户模式。

在进程之间切换

直接执行的下一个问题就是进程之间的切换。操作系统应该决定停止一个程序而开始另一个程序,这是一个比较麻烦的事情,因为一个进程在cpu上运行的时候就意味着操作系统没有运行,但如果操作系统没有运行又怎么能做事情(当然,是不能的)。

关键问题:操作系统如何重新获取cpu的控制权以便在各个进程之间切换。

协作方式:等待系统调用

早期版本的一些系统采用这样的方式:操作系统相信系统的进程会合理的运行,运行时间长的进程被假定会定期放弃cpu,以便操作系统可以决定运行其他任务。

但操作系统应该处理不当行为!如果某些程序恶意或错误的尝试执行不应该操作的事情,操作系统应该有终止这种操作的能力!如果程序执行了某些非法操作,例如应用程序以0作为除数,或者尝试访问某些无法访问的内存,就会陷入操作系统,操作系统将再次控制cpu(并可能会终止违规进程)

非协作方式:操作系统进行控制

如果没有硬件的帮助,进程拒绝进行调用而将控制权还给操作系统,那么操作系统不能再做任何事,在协作方式中,如果进程陷入无限循环,只能给内存断电——重启计算机来解决问题。

关键问题:如何在没有协作的情况下获得控制权,即便进程不协作,操作系统也应该确保恶意进程不会占用机器。

答案很简单,就是利用时钟中断,时钟设备可以编程为每隔几毫秒产生一次终端,产生中断时,当前正在运行的进程停止,操作系统中预先配置的中断处理程序会运行,此时操作系会重新获得cpu的控制权,可以停止当前进程并启动另一个进程。

即使进程以非协议方式运行,时钟中断也能让操作系统在cpu上重新运行,因此,该硬件功能对于帮助操作系统维持机器的控制权至关重要。

当然…程序中断后,需要将一定的信息保存下来,以便进行追溯和复原,在这种时候,各种寄存器被保存进入内核栈,因此从陷阱返回指令可以很容易的恢复。

保存和恢复上下文:

当操作系统获得控制权后,如果要中断一个进程或是进行切换,os就会执行一些底层代码,即所谓的上下文切换,它的概念是 操作系统把正在执行的进程保存一些寄存器的值,并为即将执行的进程恢复一些寄存器的值(这些操作都是针对内核栈进行的),这样一来,操作系统就可以确保最后执行从陷阱返回指令时,不是返回之前运行的进程,而是继续执行另一个进程。

为了保存进程的上下文,操作系统会执行一些底层的汇编代码用来保存寄存器和程序计数器一集当前运行进程的内核栈指针,然后再恢复寄存器并切换内核栈,供即将运行的进程使用,通过切换栈,内核在进入切换代码调用时,是一个被中断进程的上下文,在返回时,是即将执行的进程的上下文,当操作系统最终执行陷阱返回指令时,即将执行的进程变成了当前运行的进程——至此,上下文切换完成。

机制:受限直接执行
https://vilstia.pages.dev/posts/学习笔记/操作系统/机制受限直接执行/
作者
琴泠
发布于
2024-10-10
许可协议
CC BY-NC-SA 4.0