YueOS 开发日志 (3) - 内核篇:OS API
操作系统函数的调用接口
约定:非特权级称为用户态,特权级称为内核态。且为了安全性保证,系统初始化之后一定是使用 PSP 的用户态。
OS API 的调用
由于种种原因,用户很多时候无法执行 OS API。因此在用户态下试图访问某些系统功能,就需要使用 SVC 来向内核提出申请,并在 SVC_Handler 中实现要求。
嗯,挺好的。但是,理想很丰满,现实很骨感。这里有些细节需要考虑。
- 如何完成 SVC 到系统具体服务的映射?
- 如何实现一个统一、易于阅读且简洁优雅的 SVC 封装?(不会有人想挨个手撸汇编吧?写几行性能关键语句就得了)
- 如何保证 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 处理。
这里提供几种处理方法:
-
内核复位
当出现 Fault 时,将触发复位序列。感觉有点过于一刀切,容易导致很多问题,故排除。 -
保存 Fault 现场并陷入死循环:
当出现 Fault 时,这种方法非常直观,但也会导致系统完全失能。因此这种方法被用于调试阶段。注意为了安全性保证,系统初始化时将强制要求提供一个急停函数。当进入 fault 后,将依次执行保存现场 -> 急停 -> 死循环。
-
保存 Fault 现场并返回:
这种方法实际上是将错误处理移交给了用户,很考验用户代码的健壮性。
出于安全性考虑,系统将将定位出现错误的语句,如果多次在该语句错误,那么将以第二种方式处理。
内存管理
进程间同步/通信(IPC)
IPC 系列 API 给出了更丰富的任务调度手段。
