操作系统函数的调用接口

约定:非特权级称为用户态,特权级称为内核态。且为了安全性保证,系统初始化之后一定是使用 PSP 的用户态。

OS API 的调用

由于种种原因,用户很多时候无法执行 OS API。因此在用户态下试图访问某些系统功能,就需要使用 SVC 来向内核提出申请,并在 SVC_Handler 中实现要求。
嗯,挺好的。但是,理想很丰满,现实很骨感。这里有些细节需要考虑。

  1. 如何完成 SVC 到系统具体服务的映射?
  2. 如何实现一个统一、易于阅读且简洁优雅的 SVC 封装?(不会有人想挨个手撸汇编吧?写几行性能关键语句就得了)
  3. 如何保证 OS API 调用是可靠的?用户在中断中调用 SVC 那不炸了吗?

SVC 调用下的 OS API

当发起 SVC 请求时,一共存在 256 个被内核忽略的立即数。简单的方式是为每一个 SVC 服务分配一个立即数,但这略麻烦,我懒。
所以使用指针传递的方式。由 ARM ABI 约定易知,应该使用 R0-R3 完成实参传递且使用 R0 传回返回值,同时使用 R12 传递实调函数。
我们使用 0 号 SVC 服务作为内核调用入口,余下 255 个供用户使用。

SVC 调用封装

使用宏提供标准拓展封装(kernal_svc.h)。

OS API 的可靠调用

如何保证 OS API 是绝对可靠的?假定实际功能函数是可靠的,那安全性就由包装函数负责。
需要处理调用时的不同状态,应该确保 SVC 只有在用户态才被触发,使用几个加载及条件跳转指令即可完成。

OS API 的分类

一直以来,都没有明确界定 OS 和 OS_Kernal 的具体区别,因为偷懒任务分类中的内核任务也较为模糊,但是现在必须给出一个明确的定义了。
为保持语义的连续性,内核任务作为专有名词的意义不进行改变,即“一切需要由 OS 提供支持/完成的功能”。
而 OS API 可进一步分为

Kernal API

Kernal API 是 OS 最基础的一组 API 接口,提供了 OS 最底层任务的一组控制接口。具体而言,又可细分为

为提供足够区分度,更倾向于使用英文而非中文。

任务管理

任务管理提供了一组以任务实例为最小操作对象的 API。

  • 创建/删除
  • 静态调度表管理
  • 优先级设置
  • 任务信息的查询(包含统计信息)

时基控制与调度管理

调度器的具体实现,包含最基础的任务调度功能。

  • 任务的启动/终止/挂起/恢复
  • 被动调度算法与实现
  • 延时函数
  • 系统时间获取(DWT实现)
  • 软件定时器
  • 用于其他组件的内部超时封装

系统调用/特权操作

即特权寄存器的修改。

  • 中断与临界区相关

Fault 处理

老生常谈的四种错误,为了方便处理内核会移除局部错误处理函数。使其上访至 HardFault 处理。
这里提供几种处理方法:

  1. 内核复位
    当出现 Fault 时,将触发复位序列。感觉有点过于一刀切,容易导致很多问题,故排除。

  2. 保存 Fault 现场并陷入死循环:
    当出现 Fault 时,这种方法非常直观,但也会导致系统完全失能。因此这种方法被用于调试阶段。

    注意为了安全性保证,系统初始化时将强制要求提供一个急停函数。当进入 fault 后,将依次执行保存现场 -> 急停 -> 死循环。

  3. 保存 Fault 现场并返回:
    这种方法实际上是将错误处理移交给了用户,很考验用户代码的健壮性。
    出于安全性考虑,系统将将定位出现错误的语句,如果多次在该语句错误,那么将以第二种方式处理。

内存管理

进程间同步/通信(IPC)

IPC 系列 API 给出了更丰富的任务调度手段。

信号量

互斥量

事件

邮箱