Lec 03 系统指令集架构

Lec 03 系统指令集架构

(参考来源:上海交通大学并行与分布式系统研究所+操作系统课程ppt)
Creative Commons Attribution 4.0 License

Contents

3.1 回顾:特权级的必要性

  • 一台计算机上同时运行多个应用程序,如何保证不同应用间的隔离?
  1. 如果所有的应用均能完全控制硬件计算资源,则会导致混乱
    例如:某个应用希望关机,某个应用希望格式化硬盘
  2. 因此必须先让应用降权,不允许直接改变全局的系统状态
    例如:中断是否打开
  • 方案:必须要有不同的权限级——至少两种权限
  1. 低权限:不允许改变全局系统状态,用来运行应用
  2. 高权限:集中运行能改变全局系统状态的操作,形成了操作系统
  3. 特权操作:操作设备(读取文件、发送网络包…)、调整CPU频率、提供进程间通信…

alt

3.2 ARM v8.4特权级别(Exception Level)

alt

3.2.1 系统状态寄存器:PSTATE

  • 抽象进程状态信息(PSTATE)
  1. 条件码 (Condition flags)
    e.g. NZCV
  2. 执行状态 (Execution state controls)
    e.g. CurrentEL:CPU当前特权级别
  3. 异常掩码 (Exception mask bits)
    e.g. DAIF
  4. 访问控制(Access control bits)
    例如PAN(Privileged Access Never)

alt

3.2.2 用户ISA和系统ISA

  • 用户ISA
  1. 通用寄存器
  2. (用户)栈寄存器
  3. 条件码寄存器
  4. 运算指令等
  • 系统ISA
  1. 系统寄存器
  2. 系统指令

alt

3.2.3 用户态(EL0)与内核态(EL1)

  • 用户态(User-mode)
    1.只能使用用户 ISA
  • 内核态(Kernel-mode)
    2.可以同时使用系统 ISA 和用户ISA
  • 操作系统往往同时包含内核态与用户态的代码
    3.如:Unix包含内核态的kernel 与 用户态的 shell
  • aarch64中常见寄存器在不同特权级中的可见情况:

alt

3.3 特权级切换(EL0与EL1)

3.3.1 用户态与内核态之间的控制流跳转

  • 初始时CPU处于用户态(EL0)执行应用程序,如何改变CPU控制流从用户态进入内核态?
  1. 已知的两种改变控制流的方式:
    (1) 跳转指令,如 b
    (2) 过程调用与返回指令,如 bl 和 ret
  2. 这两种方式只能在同一种模式之间跳转
  3. 需要新的指令(在控制流跳转的同时进行特权级切换):
    例如:svceret

3.3.2 进行特权级切换的必要性

  • 操作系统的职责之一:
  1. 服务应用、管理应用
  • 特权级切换的必要性:
  1. 将CPU控制权移交给内核
  2. 服务:应用程序向操作系统请求服务
  3. 管理:操作系统能够切换不同应用程序执行
    否则,错误/恶意程序死循环怎么办(操作系统终止恶意程序(故意/无意))

3.3.3 异常处理特权级切换

  • 同步异常: 执行当前指令触发异常
  1. 第一类:用户程序主动发起:svc指令(OS利用eret指令返回)
  2. 第二类:非主动,例如用户程序意外访问空指针:普通ldr指令(OS“杀死”出错程序)
  • 异步异常: CPU收到中断信号
  1. 从外设发来的中断,例如屏幕点击、鼠标、收到网络包
  2. CPU时钟中断,例如定时器超时

alt

OS处理完异常后一定返回到被打断执行的用户程序吗?
不一定。原因如下。

3.3.4 异常处理函数

  • 异常处理函数属于操作系统的一部分
  1. 运行在内核态的代码
  • 异常处理函数完成异常处理后,将通过下述操作之一转移控制权:
  1. 回到发生异常时正在执行的指令
  2. 回到发生异常时的下一条指令
  3. 切换到其它进程执行

3.3.5 CPU寻找异常处理函数:异常向量表

  • 操作系统内核预先在一张表中准备好不同类型异常的处理函数
  1. 基地址存储在VBAR_EL1寄存器中
  2. 系统寄存器
  • CPU在异常发生时自动跳转到相应处理函数
  1. 同步异常:主动下陷svc、指令执行出错
  2. 异步异常:中断(IRQ、FIQ)、SError

alt

3.3.6 CPU执行逻辑

  • CPU的执行逻辑很简单
  1. 以PC的值为地址从内存中获取一条指令并执行
  2. PC+=4,goto 1(简化,表示跳转/函数调用)
  • 执行过程中可能发生两种情况
  1. 指令执行出现异常,比如svc、缺页(同步异常)
  2. 外部设备触发中断(异步异常)
  • 这两种情况在ARM平台均称为「异常」
  1. 均会导致CPU陷入内核态,并根据异常向量表找到对应的处理函数执行
  2. 处理函数执行完后,执行流需要恢复到之前被打断的地方继续运行

3.3.7 操作系统关于异常处理的任务

一、实现对异常向量表的设置

  1. 该设置是系统初始化的重要工作之一:在开启中断和启动第一个应用之前
  2. msr vbar_el1, x0 (是内核态才能使用的指令,内核才能访问的寄存器)

二、实现对不同异常(中断)的处理函数

  1. 处理应用程序出错的情况:如访问空指针
    Q:内核如果自己运行出错怎么办?
    A:同样内核异常处理,但无需下陷(已经处于内核态)。有些平台在三次递归后会抛出triple-fault
  2. 一类特殊的同步异常:系统调用,由应用主动触发
    Q:内核如何识别出是系统调用(而不是其他异常)?
    A:执行mrs x1, esr_el1,内核通过 ESR_EL1 寄存器读取陷入内核的原因
  3. 处理来自外部设备的中断:如收取网络包、获取键盘输入等

alt

3.4 用户态和内核态的切换

3.4.1 处理器状态变化

alt

3.4.2 处理器的任务

  1. 将发生异常事件的指令地址保存在ELR_EL1中
  2. 将异常事件的原因保存在ESR_EL1
    例如,是执行svc指令导致的,还是访存缺页导致的
  3. 将处理器的当前状态(即PSTATE)保存在SPSR_EL1
  4. 栈寄存器不再使用SP_EL0(用户态栈寄存器),开始使用SP_EL1
    内核态栈寄存器,需要由操作系统提前设置
  5. 修改PSTATE寄存器中的特权级标志位,设置为内核态
  6. 找到异常处理函数的入口地址,并将该地址写入PC,开始运行操作系统
    根据VBAR_EL1寄存器保存的异常向量表基地址,以及发生异常事件的类型确定

为什么操作系统不能直接使用应用程序在用户态的栈呢?

  • 安全问题。当操作系统的栈和用户态程序栈混合时,会使得操作系统的栈被用户态程序访问,导致内核级操作可以被应用操作。

处理器的这些操作都是必要的么?

  1. PC寄存器的值必须由处理器保存
    否则当操作系统开始执行时,PC将被覆盖
  2. 栈的切换也必须由硬件完成
    否则操作系统有可能使用用户态的栈,导致安全问题

3.4.3 eret:从内核态返回到用户态

  1. 将SPSR_EL1中的处理器状态写入PSTATE中
    处理器状态也从 EL1 切换到 EL0
  2. 栈寄存器不再使用SP_EL1,开始使用SP_EL0
    注意:SP_EL1的值并没有改变
    下一次下陷时,操作系统依然会使用这个内核栈
  3. 将ELR_EL1中的地址写入PC,并执行应用程序代码

3.4.4 操作系统在切换过程中的任务

  1. 主要任务:将属于应用程序的 CPU 状态保存到内存中
    用于之后恢复应用程序继续运行
  2. 应用程序需要保存的运行状态称为处理器上下文
    (1) 处理器上下文(Processor Context):应用程序在完成切换后恢复执行所需的最小处理器状态集合
    (2) 处理器上下文中的寄存器具体包括:
    1.通用寄存器 X0-X30
    2.特殊寄存器,主要包括PC、SP和PSTATE
    3.系统寄存器,包括页表基地址寄存器等
posted @   木木ちゃん  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示