From 689ecbcf4ba6815a49857c1edd94a6fe515d8391 Mon Sep 17 00:00:00 2001 From: Yan_yan Date: Tue, 17 Nov 2020 14:58:11 +0800 Subject: [PATCH] modify kernel/task.md --- docs/doc/kernel/task.md | 120 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/docs/doc/kernel/task.md b/docs/doc/kernel/task.md index afd99fc..a33591f 100644 --- a/docs/doc/kernel/task.md +++ b/docs/doc/kernel/task.md @@ -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 | + + +## 任务隔离 + + +### 背景及动机 + +XiUOS 是一个支持多任务的操作系统,对任务的数量没有限制。在 XiUOS 中,每个任务都需要自己的堆栈,同时也可能会动态申请内存资源。任务在运行过程中发生内存溢出是 RTOS 系统中最常见的问题,所以限制任务的内存空间访问是保证 RTOS 稳定运行的关键。 + +ARM 和 RISC-V 在体系架构上都提供了内存访问的保护功能,可以通过对特定寄存器的硬编程实现对指定内存区域访问权限的设置。然而,现有的大多数物联网操作系统并没有使用体系结构提供的内存保护功能来对任务运行的地址空间进行隔离保护。XiUOS 充分考虑任务运行的安全问题,在不影响任务正常执行的情况下,对每个任务所允许访问的内存地址空间进行限制。除此之外,任务在动态申请内存、释放内存、内存共享时也提供隔离服务。 + +XiUOS 任务隔离的总体设计思想是将物理内存地址空间划分为信任地址空间和非信任地址空间。XiUOS 的内核任务运行在信任地址空间,可以访问所有信任地址空间和非信任地址空间;XiUOS 的用户程序运行在非信任地址空间,通过”内核服务表“的方式访问内核任务提供的功能。 + + + +### 内核服务表 +ARM 和 RISC-V 在体系架构上支持机器在特权模式和非特权模式之间转换。XiUOS 的内核任务运行在特权模式下,可以访问体系结构支持的、可编程的所有硬件资源。XiUOS 的用户程序运行在非特权模式下,对硬件资源的访问权限是受限制的。为了实现用户程序受限的硬件资源访问以及内核任务提供的其他功能,XiUOS 的内核为用户程序提供一组服务接口来满足应用程序的这些需求,这一组内核服务接口称为内核服务表。应用程序访问内核服务接口的流程如下: +1. 应用程序执行异常调用指令并指定相应内核服务号和参数; +2. 通过软中断指令产生一个调用异常,之后 CPU 切换到特权模式并强制执行异常处理流程,异常处理流程提取内核服务号和参数,并将服务号作为索引; +3. 根据索引从内核服务表中查找对应的内核服务接口; +4. 在特权模式下,执行所需的内核服务,完成后切换回非特权模式继续执行。 + + +### 任务隔离表 + +在 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 +``` + + + +### 隔离机制 + +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 核提供了物理内存保护(PMP,Physical Memory Protection)功能。通过编程 PMP,可以为指定大小的内存区域设置读、写、执行等访问权限。PMP 包括 8~16 组配置寄存器和地址寄存器,一组配置寄存器和地址寄存器称为一个 PMP entry,PMP 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); + ``` + + ## 性能测试