Skip to content

受限直接执行

一、引言:虚拟化CPU的挑战

1.1 虚拟化CPU的目标

操作系统通过时分共享(time sharing)技术虚拟化CPU,使多个进程看似同时运行。基本方法是让进程轮流运行一段时间,切换到下一个进程。

1.2 关键问题

实现CPU虚拟化面临两大挑战:

  • 性能:如何在不增加系统开销的情况下实现虚拟化?
  • 控制权:如何高效运行进程,同时让操作系统保留对CPU的控制?

核心问题:如何高效、可控地虚拟化CPU?

操作系统需借助硬件支持,以高性能方式虚拟化CPU,同时确保系统控制权,避免进程无限运行或访问未授权资源。

二、受限直接执行(Limited Direct Execution, LDE)

2.1 基本思想

受限直接执行(LDE)是一种高效运行进程的技术:

  • 直接执行:直接在CPU上运行用户程序,减少开销。
  • 受限:通过硬件和操作系统机制限制进程行为,确保操作系统保留控制权。

2.2 直接执行流程

操作系统启动进程的步骤:

  1. 在进程列表中创建条目。
  2. 为程序分配内存。
  3. 从磁盘加载程序代码到内存。
  4. 设置运行时栈(包括argcargv)。
  5. 清除寄存器,调用main(),开始执行用户代码。
  6. 程序从main()返回后,操作系统释放内存,清理进程。

问题

  • 如何限制进程执行未经授权的操作(如I/O)?
  • 如何在进程运行时切换到其他进程,实现时分共享?

三、问题1:受限制的操作

3.1 挑战

关键问题:如何让进程执行受限操作(如I/O)而不完全控制系统?

直接执行允许进程高效运行,但若无限制,进程可能:

  • 绕过权限检查(如直接读写磁盘)。
  • 执行特权操作,导致系统失控。

3.2 解决方案:用户模式与内核模式

硬件提供两种执行模式:

  • 用户模式(user mode):限制进程访问硬件资源(如I/O请求被禁止,非法操作引发异常,可能终止进程)。
  • 内核模式(kernel mode):操作系统拥有完全权限,可执行特权操作。

提示受保护的控制权转移通过用户模式和内核模式的切换,配合陷阱(trap)和从陷阱返回(return-from-trap)指令,确保安全执行特权操作。

3.3 系统调用

用户程序通过系统调用执行特权操作(如访问文件、分配内存):

  • 陷阱指令(trap)
    • 程序执行陷阱指令,跳转到内核模式,运行内核代码。
    • 硬件保存调用者寄存器(如程序计数器)到进程的内核栈(kernel stack)
  • 陷阱表(trap table)
    • 内核在系统启动时设置,指定陷阱处理程序地址。
    • 硬件根据陷阱表跳转到正确代码,防止用户指定任意地址。
  • 从陷阱返回
    • 内核完成操作后,执行从陷阱返回指令,恢复寄存器,切换回用户模式,继续用户程序。

补充:系统调用为何像过程调用?

  • C库隐藏陷阱指令,封装系统调用(如open()read())。
  • 库用汇编代码按内核约定设置参数和系统调用号,执行陷阱指令,处理返回值。

3.4 陷阱表设置

  • 启动时:操作系统在内核模式下通过特权指令设置陷阱表,通知硬件处理程序地址。
  • 安全性:用户模式禁止设置陷阱表,防止恶意程序接管系统。

思考:若用户可设置陷阱表,可能跳转到内核任意代码,执行未经授权操作,甚至控制机器。

3.5 LDE协议(表6.2)

  • 启动阶段
    • 内核初始化陷阱表,硬件记住系统调用处理程序地址。
  • 运行阶段
    • 操作系统创建进程,加载程序,设置栈。
    • 从陷阱返回,切换到用户模式,跳转到main()
    • 进程调用系统调用,陷阱到内核,处理后返回。
    • 程序退出(通过exit()),内核清理资源。

四、问题2:在进程之间切换

4.1 挑战

关键问题:操作系统如何重新获得CPU控制权以切换进程?

若进程在CPU上运行,操作系统未运行,无法主动干预。需要机制确保操作系统能定期接管CPU。

4.2 协作方式

  • 方法:进程通过系统调用(如文件操作、通信)或显式yield调用主动放弃CPU。
  • 非法操作:如除以零、非法内存访问,触发陷阱,操作系统接管并可能终止进程。
  • 问题:若进程进入无限循环且不调用系统调用,操作系统无法干预,可能需重启系统。

提示:操作系统需处理不当行为(如非法操作),通常终止违规进程。

4.3 非协作方式:时钟中断

  • 时钟中断(timer interrupt)
    • 硬件每隔几毫秒触发中断,暂停当前进程,运行操作系统中断处理程序。
    • 操作系统重新获得CPU控制权,可决定继续当前进程或切换。
  • 设置
    • 启动时,操作系统设置中断处理程序地址,启动时钟(特权操作)。
    • 硬件保存进程状态(如寄存器)到内核栈,类似系统调用。
  • 安全性:用户模式禁止关闭时钟,确保操作系统控制。

提示:时钟中断是维持操作系统控制权的关键,即使进程不协作也能强制切换。

4.4 上下文切换

  • 定义:操作系统保存当前进程状态,恢复另一进程状态,切换执行。
  • 步骤
    • 保存当前进程寄存器(程序计数器、通用寄存器等)到其进程结构或内核栈。
    • 恢复目标进程寄存器,切换内核栈(指向目标进程)。
    • 执行从陷阱返回指令,运行目标进程。
  • xv6上下文切换代码(图6.1):

    assembly swtch: # 保存旧进程寄存器 movl 4(%esp), %eax # 获取旧进程context指针 popl 0(%eax) # 保存指令指针 movl %esp, 4(%eax) # 保存栈指针 movl %ebx, 8(%eax) # 保存其他寄存器 ... # 加载新进程寄存器 movl 4(%esp), %eax # 获取新进程context指针 movl 28(%eax), %ebp # 恢复寄存器 ... movl 4(%eax), %esp # 切换栈 pushl 0(%eax) # 设置返回地址 ret # 跳转到新进程

  • 寄存器保存类型

    • 硬件保存:中断时,硬件保存用户寄存器到内核栈。
    • 软件保存:操作系统保存内核寄存器到进程结构。

4.5 切换协议(表6.3)

  • 启动:初始化陷阱表,设置时钟中断处理程序,启动时钟。
  • 运行
    • 进程A运行,时钟中断触发。
    • 硬件保存进程A寄存器,进入内核模式。
    • 操作系统调用switch(),保存A的寄存器到进程结构,恢复B的寄存器,切换栈。
    • 从陷阱返回,运行进程B。

五、并发问题

5.1 潜在问题

  • 中断嵌套:系统调用处理期间发生时钟中断,或中断处理中发生另一中断,可能导致内核状态混乱。
  • 解决方法
    • 禁用中断:在中断处理期间临时禁止中断,但时间过长可能丢失中断。
    • 加锁机制:保护内核数据结构,允许多处理器并发访问(详见并发章节)。

5.2 上下文切换性能

  • 工具lmbench可测量系统调用和上下文切换时间。
  • 历史数据
    • 1996年(200-MHz P6,Linux 1.3.37):系统调用约4μs,上下文切换约6μs。
    • 现代系统(2-3 GHz CPU):亚微秒级。
  • 瓶颈:内存密集型操作受内存带宽限制,性能提升不如CPU。

六、扩展与总结

6.1 受限直接执行的核心价值

LDE通过直接执行和硬件限制结合,实现高效、可控的CPU虚拟化:

  • 高效性:程序直接运行在CPU上,减少开销。
  • 安全性:用户模式、陷阱表和时钟中断确保操作系统控制。
  • 灵活性:支持系统调用和进程切换,满足多任务需求。

类比:类似“宝宝防护”(baby proofing),操作系统通过设置陷阱和中断,限制进程访问危险资源(如特权操作),确保安全运行。

6.2 设计哲学

  • 硬件-软件协作:硬件提供模式切换、陷阱和中断,操作系统实现调度和上下文切换。
  • 模块化:陷阱表和中断处理程序独立配置,便于维护。
  • 健壮性:处理非法操作和非协作进程,确保系统稳定。

提示重启的用处

  • 重启可恢复系统到已知状态,回收泄露资源,自动化管理。
  • 在集群服务中,定期重启提高可靠性。