modify kernel/task.md

This commit is contained in:
Yan_yan 2020-11-17 14:58:11 +08:00
parent e5d124e937
commit 689ecbcf4b
1 changed files with 120 additions and 0 deletions

View File

@ -10,6 +10,11 @@
* [信号量](#sem)
* [互斥量](#mutex)
* [事件集](#event)
* [任务隔离](#isolation)
* [背景及动机](#iso_background)
* [内核服务表](#iso_kernel_service)
* [任务隔离表](#iso_task_isolation)
* [隔离机制](#iso_mechanism)
* [性能测试](#time_test)
* [概述](#time_test_intro)
* [基于 ARM 处理器的任务切换性能测试](#time_test_arm)
@ -539,6 +544,121 @@ xs_int32 xs_UserEventReinit(xs_uint16 id);
| --- | --- |
| id | 来源消息队列ID |
<span id="isolation"></span>
## 任务隔离
<span id="iso_background"></span>
### 背景及动机
XiUOS 是一个支持多任务的操作系统,对任务的数量没有限制。在 XiUOS 中,每个任务都需要自己的堆栈,同时也可能会动态申请内存资源。任务在运行过程中发生内存溢出是 RTOS 系统中最常见的问题,所以限制任务的内存空间访问是保证 RTOS 稳定运行的关键。
ARM 和 RISC-V 在体系架构上都提供了内存访问的保护功能可以通过对特定寄存器的硬编程实现对指定内存区域访问权限的设置。然而现有的大多数物联网操作系统并没有使用体系结构提供的内存保护功能来对任务运行的地址空间进行隔离保护。XiUOS 充分考虑任务运行的安全问题,在不影响任务正常执行的情况下,对每个任务所允许访问的内存地址空间进行限制。除此之外,任务在动态申请内存、释放内存、内存共享时也提供隔离服务。
XiUOS 任务隔离的总体设计思想是将物理内存地址空间划分为信任地址空间和非信任地址空间。XiUOS 的内核任务运行在信任地址空间可以访问所有信任地址空间和非信任地址空间XiUOS 的用户程序运行在非信任地址空间,通过”内核服务表“的方式访问内核任务提供的功能。
<span id="iso_kernel_service"></span>
### 内核服务表
ARM 和 RISC-V 在体系架构上支持机器在特权模式和非特权模式之间转换。XiUOS 的内核任务运行在特权模式下可以访问体系结构支持的、可编程的所有硬件资源。XiUOS 的用户程序运行在非特权模式下对硬件资源的访问权限是受限制的。为了实现用户程序受限的硬件资源访问以及内核任务提供的其他功能XiUOS 的内核为用户程序提供一组服务接口来满足应用程序的这些需求,这一组内核服务接口称为<B>内核服务表</B>。应用程序访问内核服务接口的流程如下:
1. 应用程序执行异常调用指令并指定相应内核服务号和参数;
2. 通过软中断指令产生一个调用异常,之后 CPU 切换到特权模式并强制执行异常处理流程,异常处理流程提取内核服务号和参数,并将服务号作为索引;
3. 根据索引从内核服务表中查找对应的内核服务接口;
4. 在特权模式下,执行所需的内核服务,完成后切换回非特权模式继续执行。
<span id="iso_task_isolation"></span>
### 任务隔离表
在 XiUOS 中,任务描述符 task_descriptor 管理系统的任务其中包含了任务优先级、任务名称、任务状态等信息。为了管理任务可访问的内存空间task_descriptor 描述符在 dync_sched_member 子结构中增加任务隔离标志位 isolation_flag 和任务隔离表 isolation_table, 其定义如下:
```c
struct dync_sched_member
{
#ifdef XS_USING_TASK_ISOLATION
xs_uint8 isolation_flag;
void *isolation_table;
#endif
};
```
用户程序运行在非信任地址空间,默认开启隔离机制。在用户程序对应的任务被创建时,任务隔离标志位 isolation_flag 被置为1其允许访问的地址空间范围由 isolation_table 指针所指定。isolation_table 包含了任务可访问的多个内存地址空间,每个内存地址空间用一个 isolation_region 数据结构来描述,这个数据结构包括一段连续的地址空间范围和访问权限,其具体结构取决于体系架构提供的内存保护功能,如 ARM 提供的 MPU 和 RISC-V 提供的 PMP 功能。isolation_region 的定义如下:
```c
#if defined(XS_USING_TASK_ISOLATION)
typedef struct isolation_region
{
#ifdefined (XS_RISCV32)
xs_uint8 region_cfg;
xs_uint32 region_addr;
#elif defined (XS_RISCV64)
xs_uint8 region_cfg;
xs_uint64 region_addr;
#elif defined (XS_ARM32)
xs_uint32 region_cfg;
xs_uint32 region_addr;
#endif
}isolation_region;
#endif
```
<span id="iso_mechanism"></span>
### 隔离机制
1. 任务内存结构分布及 isolation_region
XiUOS 中的任务在编译链接后形成Linux通用的ELF文件结构其中包括.code、.data、.bss等段。在任务加载过程中.code、.data、.bss会被加载到对应的内存段中。在创建任务时先判断任务类型xs_UserTaskCreate用于创建任务其中 isolation_flag 标志位会被置为1。当为任务分配好栈空间后isolation_table 中为.code、.data、.bss 等段分别创建一个 isolation_region并设置对应的地址范围和访问权限如.code段对应的 isolation_region 为读和执行权限,.data 段对应的 isolation_region 为只读权限,.bss 段和栈对应的 isolation_region 为读写权限。
2. 任务切换时的隔离
在 XiUOS 中,多个任务共享有限个 CPU 核,采用优先级加时间片的调度方式,当一个任务时间片耗尽或者主动让出 CPU 的使用权时,内核调度程序负责保存当前任务的上下文信息,并从等待队列中挑选下一个就绪任务,恢复其上下文信息,并允许其获取 CPU 的使用权。为了保证每个任务只能访问自己的内存空间,内核调度程序在恢复任务上下文时,会先根据 isolation_flag 标志判断该任务是否为用户程序,如果为用户程序,则当前 CPU 核在运行该任务时,只允许访问本任务 isolation_table 中定义的内存区域,对于其它区域没有访问权限。
3. 动态申请/释放内存时的隔离
XiUOS 的任务通过 malloc/free 等内核服务接口来动态申请和释放内存空间,并能根据用户任务申请和释放的内存地址更新任务隔离表。当用户程序通过 malloc 等内核服务接口向内核申请指定大小的内存空间时,系统会在用户程序对应的任务中增加对内存空间的访问权限,同时将更新后的任务隔离表加载到内存保护单元配置寄存器中,使其生效。当用户程序通过 free 等内核服务接口向内核释放指定大小的内存空间时,系统会在用户程序对应的任务隔离表中清除对这段内存空间的访问权限,同时加载更新后的任务隔离表到内存保护单元寄存器中,使其生效。
此外XiUOS 还支持共享内存的任务隔离,其基本思路同动态内存申请/释放时相同。进一步地当用户程序因为某种原因试图访问没有权限的内存空间时CPU 会产生一个访问错误的异常,并进入内核服务接口的异常处理流程,在这个流程中会将该任务直接杀死并回收任务资源。
4. RISC-V 64架构任务隔离
大部分 RISC-V 架构的 CPU 拥有特权模式和非特权模式。这两种模式都为 CPU 核提供了物理内存保护PMPPhysical Memory Protection功能。通过编程 PMP可以为指定大小的内存区域设置读、写、执行等访问权限。PMP 包括 8~16 组配置寄存器和地址寄存器,一组配置寄存器和地址寄存器称为一个 PMP entryPMP entry 对应之前定义的 isolation_region用于标识一段内存地址空间的访问权限其结构定义如下
对于一个正在运行用户任务其isolation_table的内容大致如下
```c
isolation_table[16] =
{
{ //.code
.region_cfg = __PMP_REGION_CFG( 0, // L=0用户任务遵守RWX指定的权限内核任务拥有全部权限
.region_addr = __PMP_REGION_ADDR(text_start_addr,region_start_addr_size);
},
{ //.data
.region_cfg = __PMP_REGION_CFG( 0, //L=0用户任务遵守RWX指定的权限内核任务拥有全部权限
.region_addr = __PMP_REGION_ADDR(data_start_addr,region_start_addr_size);
},
{ //.bss段
.region_cfg = __PMP_REGION_CFG( 0, //L=0用户任务遵守RWX指定的权限内核任务拥有全部权限
.region_addr = __PMP_REGION_ADDR(bss_start_addr,region_start_addr_size);
},
{ // stack
.region_cfg = __PMP_REGIONP_CFG( 0,//L=0用户任务遵守RWX指定的权限内核任务拥有全部权限
.region_addr = __PMP_REGION_ADDR(stack_start_addr,region_start_addr_size);
},
};
```
相关PMP操作的接口如下
```c
// isolation_table增加一个region
PMP_add_region(isolation_region *isolation_table, void *region_address,xs_size_t size, int flag );
// isolation_table增加清除一个region
PMP_clear_region(isolation_region *isolation_table, void *region_address,xs_size_t size);
// 将isolation_table加载到PMP中
PMP_load(isolation_region *isolation_table , xs_uint8 coreid);
```
5. ARM-32 架构任务隔离
ARM32 的 handler mode 和 thread mode 分别对应特权模式和非特权模式。ARM32 架构的 MPU 单元可以对内存地址空间的访问权限进行设置从而实现任务地址空间的隔离访问。MPU 通过将内存空间划分为多个 “region” 进行权限设置,一个 region 就是一段连续的地址空间,对应之前定义的 isolation_region一般 MPU 支持设置8~16个regions。在启用MPU后程序就无法访问定义之外的地址区间也不得访问未经授权的region否则将会触发内存访问错误。对于正在运的行用户任务ARM32 的 isolation_table 的内容同 RISC-V 架构基本相同。
相关MPU的操作接口如下
```c
// isolation_table增加一个region
MPU_add_region(isolation_region *isolation_table, void *region_address, xs_size_t size, int flag );
// isolation_table增加清除一个region
MPU_clear_region(isolation_region *isolation_table, void *region_address, xs_size_t size);
// 将isolation_table加载到MPU中
MPU_load(isolation_region *isolation_table, xs_uint8 coreid);
```
<span id="time_test"></span>
## 性能测试