sentence sematic modification

This commit is contained in:
Yan_yan 2020-11-24 14:57:58 +08:00
parent 742ec0d8a9
commit c06e959246
5 changed files with 219 additions and 107 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

View File

@ -27,85 +27,76 @@
## 任务机制
任务task是XiUOS中处理器使用权分配的最小单位。每个任务有自己的程序栈与寄存器上下文在多处理器平台上可以互不干扰地同时运行但单个处理器上任意时刻只能有一个任务在运行。用户可以使用XiUOS提供的接口创建任意数量的任务。内核会对系统中的所有任务按照一定策略抢占式优先级或先来先服务进行调度最大限度地利用处理器资源。用户可以使用XiUOS提供的接口创建任意数量的任务。
任务task XiUOS 中处理器使用权分配的最小单位。每个任务有自己的程序栈与寄存器上下文,在多处理器平台上可以互不干扰地同时运行,但单个处理器上任意时刻只能有一个任务在运行。用户可以使用 XiUOS 提供的接口创建任意数量的任务。内核会对系统中的所有任务按照一定策略(抢占式优先级或先来先服务)进行调度,最大限度地利用处理器资源。
<span id="state"></span>
### 任务状态
系统中的任务在任意时刻都处于就绪ready、运行running、阻塞/挂起suspend、退出quit四种状态之一。状态之间的变化关系如下图所示。
XiUOS 中的任务在任意时刻都处于就绪ready、运行running、阻塞/挂起suspend、退出quit四种状态之一。状态之间的变化关系如下图所示。
<img src="./imagesrc/task_status.png" width =100%/>
任务在创建完成后会进入就绪状态并被加入就绪队列等待内核调度。当任务被调度开始运行时,任务会进入运行状态。若任务在运行过程中被更高优先级的任务抢占,则被抢占的任务会回到就绪队列并再次进入就绪状态。当任务在运行过程中申请资源失败时,任务会被挂起并进入挂起状态,并在所申请资源能够被满足时回到就绪状态。当任务执行完成,即从入口函数返回时,会进入终止状态,并由内核回收其相关资源。
* 任务在创建完成后会进入<B>就绪状态</B>并被加入就绪队列等待内核 CPU 调度
* 当任务被调度开始运行时,任务会进入<B>运行状态</B>
* 若任务在运行过程中被更高优先级的任务抢占,则被抢占的任务会回到就绪队列并再次进入<B>就绪状态</B>
* 当任务在运行过程中申请资源失败时,任务会被挂起并进入<B>挂起状态</B>,并在所申请资源能够被满足时回到<B>就绪状态</B>
* 当任务执行完成,即从入口函数返回时,会进入<B>终止状态</B>,并由内核回收其相关资源
<span id="sched"></span>
### 任务调度
任务调度即从系统就绪队列中按一定策略选择任务使其进入运行状态的过程。XiUOS支持以下调度方式
任务调度即从系统就绪队列中按一定策略选择某一任务使其进入运行状态的过程。XiUOS 支持以下调度方式:
* 抢占式优先级调度:在创建任务时可以指定任务的优先级,内核总是选取就绪队列中优先级最高的任务。当新创建的任务优先级高于正在运行的任务的优先级时,当前运行任务会被抢占。若就绪队列中最高优先级任务有多个,则这些任务会按时间片轮转交替运行。
* 先来先服务FIFO调度任务按照被创建的顺序依次被执行。当一个任务运行完成后,系统才会让下一个任务开始运行。
* 时间片轮转RR调度任务按照分配的时间片执行,时间片结束,调度一个新的就绪任务执行,当前任务重新就绪,等待下一次的调度。
<span id="struct"></span>
* 抢占式优先级调度,即在创建任务时,用户可以指定任务的优先级,内核总是选取就绪队列中优先级最高的任务。当新创建的任务优先级高于正在运行的任务的优先级时,当前运行任务的CPU使用权会被抢占。若就绪队列中最高优先级任务有多个,则这些任务会按时间片轮转方式交替运行。
* 先来先服务FIFO调度,即任务按照被创建的顺序依次被执行。当一个任务运行完成后,系统才会让下一个任务开始运行。
* 时间片轮转RR调度,即任务按照分配的时间片执行,时间片结束,调度一个新的就绪任务执行,当前任务重新就绪,等待下一次的调度。
<span id="struct"></span>
### 任务结构定义
每个任务在内核中由一个task_descriptor结构表示二者一一对应。
每一个任务在内核中都有一个task_descriptor描述符其结构体定义如下
```c
struct task_descriptor
{
void *stack_pointer;
void *stack_pointer;
struct dync_sched_member dync_sched_member_x;
struct t_baseinfo t_baseinfo_x;
struct task_baseinfo t_baseinfo_x;
#ifdef XS_USING_SMP
struct smp_info smp_info_x;
struct smp_info smp_info_x;
#endif
#if defined(XS_USING_EVENT)
xs_uint32 event_ctrl1:3;
xs_uint32 event_ctrl2:29;
xs_uint32 event_ctrl1:3;
xs_uint32 event_ctrl2:29;
#endif
xs_err_t error;
XS_DOUBLE_LINKLIST link;
xs_err_t error;
XS_DOUBLE_LINKLIST link;
};
```
其中stack_pointer成员表示该任务的栈的起始地址dync_sched_member_x成员包含与任务调度相关的信息t_baseinfo_x包含任务的基本信息smp_info_x包含与多处理器相关的信息event_ctrl1/event_ctrl2用于实现事件集机制见任务间通信部分文档error为任务调用内核接口时最近的错误码link用于将系统中的所有任务组织成一个双链表。各复合成员的详细定义如下
```c
struct task_baseinfo {
char name[XS_NAME_MAX];
void *func_entry;
void *func_param;
xs_uint32 stack_size;
xs_uint8 origin_prio;
};
```
struct task_baseinfo结构记录了任务的基本属性包括任务的名称name、入口函数和参数func_entry、func_param、栈大小stack_size、优先级origin_prio
其中stack_pointer 指向任务堆栈的起始地址dync_sched_member_x 包含与任务调度相关的信息t_baseinfo_x 记录任务的基本信息smp_info_x 统计与多处理器相关的信息event_ctrl1 / event_ctrl2 用于实现事件集机制(详见[任务通信](#communication)error为任务调用内核接口时最近的错误码即用户线程在使用内核接口时可能会执行失败此时内核接口返回-1具体的错误码被保存在error成员中且在下一次调用内核接口失败时被覆盖link 用于组织内核中所有的任务。各复合成员的详细定义如下。
* struct dync_sched_member
```c
struct dync_sched_member {
xs_uint8 stat;
xs_uint8 advance_cnt;
xs_uint8 cur_prio;
xs_ubase origin_timeslice;
xs_ubase rest_timeslice;
xs_uint8 stat;
xs_uint8 advance_cnt;
xs_uint8 cur_prio;
xs_ubase origin_timeslice;
xs_ubase rest_timeslice;
#ifdef XS_USING_TASK_ISOLATION
xs_uint8 isolation_flag;
void *isolation;
xs_uint8 isolation_flag;
void *isolation;
#endif
union {
XS_DOUBLE_LINKLIST sched_link;
XS_AVL sched_avl;
}
XS_AVL sched_avl;
};
XS_DOUBLE_LINKLIST pend_link;
xs_Timer_t task_timer;
xs_Timer_t task_timer;
};
#define XS_SUSPEND ((1) << (0))
@ -114,15 +105,26 @@ struct dync_sched_member {
#define XS_QUIT ((1) << (3))
```
struct dync_sched_member结构的成员用于记录与调度相关的信息。stat表示任务的当前状态可以为挂起XS_SUSPEND、就绪XS_READY、运行XS_RUNNING或退出XS_QUIT。advance_cnt表示在配置成短作业预先调度时优先处理的时间片周期个数。cur_prio表示任务当前的优先级为防止出现优先级反转该优先级可以高于任务创建时配置的优先级。origin_timeslice表示在时间片轮转调度中该任务每次运行的时间片。地址空间隔离信息isolation_flag变量和指针isolation。sched_link、sched_avl构成的联合体为就绪队列节点XiUOS中就绪队列可以配置为双链表sched_link或AVL树sched_avl。pend_link为任务挂起时使用的等待队列节点。task_timer用于任务睡眠的计时。
struct dync_sched_member结构用于记录与调度相关的信息。stat记录任务的当前状态可以为挂起XS_SUSPEND、就绪XS_READY、运行XS_RUNNING或退出XS_QUIT。advance_cnt表示在配置成短作业预先调度时优先处理的时间片周期个数。cur_prio表示任务当前的优先级用于优先级反转该优先级可以高于任务创建时配置的优先级。origin_timeslice表示在时间片轮转调度时任务每次运行的时间片。isolation_flag变量和指针isolation支持地址空间隔离。sched_link和sched_avl构成的联合体为就绪队列节点XiUOS中就绪队列可以组织为双链表sched_link或平衡二叉树sched_avl。pend_link为任务挂起时使用的等待队列节点。task_timer为任务睡眠的计数器。
* struct task_baseinfo
```c
struct task_baseinfo {
char name[XS_NAME_MAX];
void *func_entry;
void *func_param;
xs_uint32 stack_size;
xs_uint8 origin_prio;
};
```
struct task_baseinfo结构记录了任务的基本属性包括任务的名称name、入口函数func_entry和参数func_param、栈大小stack_size、初始优先级origin_prio
* struct smp_info
```c
struct smp_info {
xs_uint8 combined_coreid;
xs_uint8 running_coreid;
};
```
struct smp_info结构包含多处理器相关的信息其成员分别表示该任务绑定的CPU ID与正在运行的CPU ID。
<span id="api"></span>
@ -142,7 +144,7 @@ typedef struct xs_utask xs_utask_x;
struct task_descriptor* xs_UserTaskCreate(xs_utask_x task);
```
该函数用于用户态创建一个任务。任务的各个属性由一个struct xs_utask结构表示包括任务的名称、入口函数及参数、栈大小和优先级在调用该函数时需要传入该结构的实例用于配置任务。任务创建成功后内核会为其分配指定大小的栈及其他结构如struct task_descriptor结构,并返回任务描述符指针。
该函数用于用户态的任务创建。任务的各个属性由struct xs_utask结构表示包括任务的名称、入口函数及参数、栈大小和优先级在调用该函数时需要传入该结构的实例用于配置任务属性。任务创建成功后内核会为其分配指定大小的栈及其他结构如struct task_descriptor并返回任务描述符指针。
| 参数 | 描述 |
| --- | --- |
@ -152,28 +154,28 @@ struct task_descriptor* xs_UserTaskCreate(xs_utask_x task);
xs_int32 xs_UserTaskDelete(struct task_descriptor task);
```
该函数用于删除一个任务,强制使其进入退出状态。若删除成功则返回XS_EOK若失败则返回-XS_ERROR。
该函数用于删除一个任务强制其进入退出状态。若删除成功则返回XS_EOK则返回-XS_ERROR。
| 参数 | 描述 |
| --- | --- |
| task| 待删除的任务描述符 |
```c
xs_int32 xs_UserTaskCoreCombine(struct task_descriptor task, xs_uint8 coreid);
xs_int32 xs_UserTaskCoreCombine(struct task_descriptor task_id, xs_uint8 core_id);
```
该函数用于将任务绑定至指定的处理器上。若绑定成功则返回XS_EOK若失败则返回-XS_ERROR。
该函数用于将任务绑定至指定的处理器上。若绑定成功则返回XS_EOK则返回-XS_ERROR。
| 参数 | 描述 |
| --- | --- |
| task_id | 待绑定的任务描述符 |
| coreid | 待绑定的处理器ID |
| core_id | 待绑定的处理器ID |
```c
xs_int32 xs_UserTaskCoreUncombine(struct task_descriptor task);
```
该函数用于解除任务与处理器的绑定。若解除成功则返回XS_EOK若失败则返回-XS_ERROR。
该函数用于解除任务与处理器的绑定。若解除成功则返回XS_EOK则返回-XS_ERROR。
| 参数 | 描述 |
| --- | --- |
@ -183,7 +185,7 @@ xs_int32 xs_UserTaskCoreUncombine(struct task_descriptor task);
xs_int32 xs_UserTaskDelay(xs_ticks_x ticks);
```
该函数用于将当前任务挂起一定时间单位为tick。挂起时间结束后任务会进入就绪状态可以继续被调度
该函数用于将当前任务挂起一定时间单位为tick。挂起时间结束后任务会进入就绪状态等待系统调用
| 参数 | 描述 |
| --- | --- |
@ -193,18 +195,16 @@ xs_int32 xs_UserTaskDelay(xs_ticks_x ticks);
struct task_descriptor* xs_UserTaskSearch(char *name)
```
该函数用于从任务名称获取任务描述符。
该函数根据任务名称获取任务描述符。
| 参数 | 描述 |
| --- | --- |
| name | 任务名称 |
<span id="communication"></span>
## 任务通信
XiUOS提供多种任务间通信机制包括消息队列、信号量、互斥量与事件集。
XiUOS 提供多种任务间通信机制,包括消息队列、信号量、互斥量与事件集。
<span id="msg_quque"></span>
### 消息队列
@ -299,12 +299,12 @@ xs_int32 xs_UserMsgQueueReinit(xs_uint16 id);
| 参数 | 描述 |
| --- | --- |
| id | 来源消息队列ID |
| id | 消息队列ID |
<span id="sem"></span>
### 信号量
信号量semaphore具有一个给定的初值。任务可以获取或释放一个信号量。当任务获取信号量时信号量的值递减释放信号量时值递增。当信号量值递减至0时后续尝试获取信号量的任务会被挂起。一个已获取信号量的任务释放信号量时,内核会从信号量挂起队列上唤醒一个任务。信号量可以用于实现任务间的同步与互斥。
信号量semaphore具有一个给定的初值。任务可以获取或释放一个信号量。当任务获取信号量时信号量的值递减,释放信号量时,信号量的值递增。当信号量值递减至0时后续尝试获取信号量的任务会被挂起。当任务释放信号量时内核会从信号量挂起队列上唤醒一个任务。信号量可以实现任务间的同步与互斥。
#### 信号量结构定义
@ -346,7 +346,7 @@ void xs_UserSemaphoreDelete(xs_uint16 id);
| 参数 | 描述 |
| --- | --- |
| id | 待删除的信号量ID |
| id | 待删除的信号量ID |
```c
xs_int32 xs_UserSemaphoreObtain(xs_uint16 id, xs_ticks_x wait_time);
@ -356,7 +356,7 @@ xs_int32 xs_UserSemaphoreObtain(xs_uint16 id, xs_ticks_x wait_time);
| 参数 | 描述 |
| --- | --- |
| id | 欲获取的信号量ID |
| id | 欲获取的信号量ID |
| wait_time | 等待时间上限单位ticks若为0则不等待 |
```c
@ -367,7 +367,7 @@ xs_int32 xs_UserSemaphoreAbandon(xs_uint16 id);
| 参数 | 描述 |
| --- | --- |
| id | 待释放的信号量ID |
| id | 待释放的信号量ID |
```c
xs_int32 xs_UserSemaphoreSetValue(xs_uint16 id, xs_uint16 val);
@ -452,7 +452,7 @@ xs_int32 xs_UserMutexAbandon(xs_uint16 id);
### 事件集
事件集event set允许用户定义一个事件集合集合中的每个事件都可以被任务触发或等待。任务可以同时等待多个事件此时等待触发的条件可以配置为AND或者OR当等待触发条件配置为AND时只有所有被等待的事件均被触发才视作等待结束当等待触发条件配置为OR时任意一个被等待的事件触发即视作等待结束。使用事件集可以实现多对多的任务间同步与互斥。
事件集event set允许用户定义一个事件集合集合中的每个事件都可以被任务触发或等待。任务可以同时等待多个事件此时等待触发的条件可以配置为 AND 或者 OR :当等待触发条件配置为 AND 时,只有所有被等待的事件均被触发才视作等待结束;当等待触发条件配置为 OR 时,任意一个被等待的事件触发即视作等待结束。使用事件集可以实现多对多的任务间同步与互斥。
#### 事件集结构定义
@ -465,7 +465,7 @@ xs_int32 xs_UserMutexAbandon(xs_uint16 id);
struct xs_event
{
xs_uint16 id;
xs_uint32 trigge_way;
xs_uint32 trigger_way;
XS_DOUBLE_LINKLIST pend_list;
XS_DOUBLE_LINKLIST link;
@ -475,7 +475,7 @@ struct xs_event
| 成员 | 描述 |
| --- | --- |
| id | 事件集ID用于唯一标识一个事件集 |
| trigge_way | 高3位用于记录事件集属性等待触发条件、是否自动清空低29位用于表示至多29个事件 |
| trigger_way | 高3位用于记录事件集属性等待触发条件、是否自动清空低29位用于表示至多29个事件 |
| pend_list | 等待任务链表 |
| link | 系统中所有事件集构成的链表 |
@ -485,11 +485,11 @@ struct xs_event
xs_int32 xs_UserEventCreate(xs_uint32 options);
```
该函数用于创建一个事件集。options参数用于配置事件集的属性。可配置的属性有等待触发方式XS_EVENT_AND或XS_EVENT_OR及等待触发后是否自动清空其他已触发的事件XS_EVENT_AUTOCLEAN。创建成功后新的事件集会被加入内核的事件集管理链表并返回该事件集的IDID默认范围0-255可配置。
该函数用于创建一个事件集。options参数用于配置事件集的属性。可配置的属性有等待触发方式XS_EVENT_AND XS_EVENT_OR及等待触发后是否自动清空其他已触发的事件XS_EVENT_AUTOCLEAN。创建成功后新的事件集会被加入内核的事件集管理链表并返回该事件集的IDID默认范围0-255可配置。
| 参数 | 描述 |
| --- | --- |
| options | 事件集配置选项须在XS_EVENT_AND及XS_EVENT_OR中指定其一并可以按位或上XS_EVENT_AUTOCLEAN |
| options | 事件集配置选项,须在 XS_EVENT_AND XS_EVENT_OR 中指定其一,并可以按位或上 XS_EVENT_AUTOCLEAN |
```c
void xs_UserEventDele(xs_uint16 id);
@ -509,13 +509,13 @@ xs_int32 xs_UserEventTrigger(xs_uint16 id, xs_uint32 events);
| 参数 | 描述 |
| --- | --- |
| id | 事件集ID |
| events | 欲触发的事件其中被置1的位标识被触发的事件 ,可以使用XS_EVENTS宏按位或发送事件类型|
| events | 欲触发的事件其中被置1的位标识被触发的事件 ,可以使用 XS_EVENTS 宏按位或发送事件类型|
```c
xs_int32 xs_UserEventProcess(xs_uint16 id, xs_uint32 events, xs_ticks_x wait_time);
```
该函数用于等待事件集中的一组事件。若等待成功则返回XS_EOK此时若XS_EVENT_AUTOCLEAN被打开则事件集中所有已触发事件会被清空若等待失败超时则返回-XS_ETIMEOUT。
该函数用于等待事件集中的一组事件。若等待成功则返回 XS_EOK此时若 XS_EVENT_AUTOCLEAN 被打开则事件集中所有已触发事件会被清空;若等待失败(超时)则返回 -XS_ETIMEOUT。
| 参数 | 描述 |
| --- | --- |
@ -527,12 +527,12 @@ xs_int32 xs_UserEventProcess(xs_uint16 id, xs_uint32 events, xs_ticks_x wait_tim
xs_int32 xs_UserEventConfig(xs_uint16 id, xs_uint32 options);
```
该函数用于配置事件集中的一种事件类型。若配置成功则返回XS_EOKoptions参数用于配置事件集的属性。可配置的属性有等待触发方式XS_EVENT_AND或XS_EVENT_OR及等待触发后是否自动清空其他已触发的事件XS_EVENT_AUTOCLEAN
该函数用于配置事件集中的一种事件类型。若配置成功则返回 XS_EOKoptions 参数用于配置事件集的属性。可配置的属性有等待触发方式XS_EVENT_AND XS_EVENT_OR及等待触发后是否自动清空其他已触发的事件XS_EVENT_AUTOCLEAN
| 参数 | 描述 |
| --- | --- |
| id | 事件集ID |
| options | 事件集配置选项须在XS_EVENT_AND及XS_EVENT_OR中指定其一并可以按位或上XS_EVENT_AUTOCLEAN |
| options | 事件集配置选项,须在 XS_EVENT_AND XS_EVENT_OR 中指定其一,并可以按位或上 XS_EVENT_AUTOCLEAN |
```c
xs_int32 xs_UserEventReinit(xs_uint16 id);
@ -613,7 +613,7 @@ XiUOS 中的任务在编译链接后形成Linux通用的ELF文件结构其中
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用于标识一段内存地址空间的访问权限,其结构定义如下:
大部分 RISC-V 架构的 CPU 拥有特权模式和非特权模式。这两种模式都为 CPU 核提供了物理内存保护PMPPhysical Memory Protection功能。通过编程 PMP可以为指定大小的内存区域设置读、写、执行等访问权限。PMP 包括 8~16 组配置寄存器和地址寄存器,一组配置寄存器和地址寄存器称为一个 PMP entryPMP entry 对应之前定义的 isolation_region用于标识一段内存地址空间的访问权限
对于一个正在运行用户任务其isolation_table的内容大致如下
```c
isolation_table[16] =
@ -639,22 +639,22 @@ isolation_table[16] =
```
相关PMP操作的接口如下
```c
// isolation_table增加一个region
PMP_add_region(isolation_region *isolation_table, void *region_address,xs_size_t size, int flag );
// isolation_table增加清除一个region
// 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);
// 将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
// 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中
// 将isolation_table加载到MPU中
MPU_load(isolation_region *isolation_table, xs_uint8 coreid);
```
@ -666,7 +666,7 @@ MPU_load(isolation_region *isolation_table, xs_uint8 coreid);
<span id="time_test_intro"></span>
### 概述
下面分别测试XiUOS系统运行在基于ARM和RISC-V不同处理器的开发板时任务的切换时间。
下面分别测试XiUOS系统运行在基于 ARM RISC-V 不同处理器的开发板时,任务的切换时间。
<span id="time_test_arm"></span>
@ -690,25 +690,95 @@ XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext
<center>
![RISCV TEST CONNECT](./imagesrc/arm_test_code.png)
```c
void realtime_taskswitch_test()
{
xs_PinMode(GPIO_C13, PIN_MODE_OUTPUT);
xs_PinWrite(GPIO_C13, PIN_LOW);
while(1){
xs_DelayKThread(1);
}
}
```
</center>
因为测试单板为cortex-m4单板该系列单板的线程切换是基于pendSV CPU异常进行线程切换因此下面基于该特点区分测试场景
* 测量pendSV异常切换 在SwitchKthreadContext函数入口处将C13管脚置为高电平在pendSV异常处理过程保存现场之后切换到目标任务之前将C13管脚置为低电平。得出的管脚电平时间即为带pendSV异常的的任务切换时间。
* 只测了SwitchKthreadContext在SwitchKthreadContext函数入口处将C13管脚置为高电平在出口位置将C13管脚置为低电平。得出的管脚电平时间即为不计算pendSV异常的的任务切换时间。
* 测量pendSV异常切换在SwitchKthreadContext函数入口处将C13管脚置为高电平在pendSV异常处理过程保存现场之后切换到目标任务之前将C13管脚置为低电平。得出的管脚电平时间即为带pendSV异常的的任务切换时间。
* 只测了SwitchKthreadContext在SwitchKthreadContext函数入口处将C13管脚置为高电平在出口位置将C13管脚置为低电平。得出的管脚电平时间即为不计算pendSV异常的的任务切换时间。
<center>
```c
xs_SwitchKthreadContext:
/* 将GPIO C13置为高电平 */
LDR r2, =0x40020818 // 测试代码
MOV r3, #0x2000 // 测试代码
STR r3, [r2] // 测试代码
![RISCV TEST CONNECT](./imagesrc/arm_test_switch.png)
LDR r2, =xs_KthreadSwitchInterruptFlag
LDR r3, [r2]
CMP r3, #1
BEQ _reswitch
MOV r3, #1
STR r3, [r2]
</center>
LDR r2, =xs_InterruptFromKthread
STR r0, [r2]
<center>
_reswitch:
LDR r2, =xs_InterruptToKthread
STR r1, [r2]
![RISCV TEST CONNECT](./imagesrc/arm_test_switch1.png)
LDR r0, =NVIC_INT_CTRL
LDR r2, =NVIC_PENDSVSET
STR r1, [r0]
</center>
/* 将GPIO C13置为低电平 */
LDR r2, =0x4002081a // 测试代码
MOV r3, #0x2000 // 测试代码
STR r3, [r2] // 测试代码
BX LR
.global PendSV_Handler
.type PendSV_Handler, %function
PendSV_Handler:
MRS r2, PRIMASK
CPSID I
LDR r0, =xs_KthreadSwitchInterruptFlag
LDR r1, [r0]
CBZ r1, pendsv_exit
MOV r1, #0x00
STR r1, [r0]
LDR r0, =xs_InterruptFromKthread
LDR r1, [r0]
CBZ r1, switch_to_thread
MRS r1, psp
STMFD r1!, {r4 - r11}
#if defined (__VFP_FP__) && !defined(__SOFTFP__)
MOV r4, #0x00
TST lr, #0x10
MOVEQ r4, #0x01
STMFD r1!, {r4}
#endif
LDR r0, [r0]
STR r1, [r0]
switch_to_thread:
/* 将GPIO C13置为低电平 */
LDR r2, =0x4002081a // 测试代码
MOV r3, #0x2000 // 测试代码
STR r3, [r2] // 测试代码
```
#### 示波器测试选项设置
@ -720,7 +790,7 @@ XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext
* 反相:关闭
* 触发设置
* 类型:边沿
* 信源: CH1
* 信源 CH1
* 斜率:上升
* 模式:自动
* 触发电压:略低于最高电平即可
@ -738,14 +808,14 @@ XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext
</center>
从示波器测试结果上来看单独测试SwitchKthreadContext的执行时间是1.26us。
从示波器测试结果上来看,单独测试 SwitchKthreadContext 的执行时间是1.26us。
<center>
![RISCV TEST CONNECT](./imagesrc/arm_test_result1.png)
</center>
从示波器测试结果上来看测试SwitchKthreadContext加上pendSV异常的的执行时间是17us。
从示波器测试结果上来看,测试 SwitchKthreadContext 加上 pendSV 异常的的执行时间是17us。
<span id="time_test_riscv"></span>
@ -763,23 +833,65 @@ XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext
</center>
XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext函数入口位置将GPIO18管脚置为高电平出口位置置为低电平;则GPIO18管脚保持高电平的时间即切换时间。
XiUOS的任务切换函数为 xs_SwitchKthreadContext SwitchKthreadContext 函数入口位置将 GPIO18 管脚置为高电平,出口位置置为低电平则GPIO18管脚保持高电平的时间即切换时间。
#### 编程代码清单
<center>
```c
void realtime_taskswitch_test()
{
xs_PinMode(GPIO_18, PIN_MODE_OUTPUT);
xs_PinWrite(GPIO_18, PIN_LOW);
while(1){
xs_DelayKThread(1);
}
}
```
![RISCV TEST CONNECT](./imagesrc/riscv_test_code.png)
</center>
初始化GPIO18为输出模式并初始化为低电平在while1当中调用delay函数每隔1个时间片发生一次调度。在下面的switch函数入口和出口位置操作GPIO。
<center>
```c
.global xs_SwitchKthreadContext
xs_SwitchKthreadContext:
/* 将 GPIO18 置为高电平 */
lui a5, 0x38001 // 测试代码
addi a5, a5, 12 // 测试代码
lw a5, 0(a5) // 测试代码
sext.w a4, a5 // 测试代码
lui a5, 0x38001 // 测试代码
addi a5, a5, 12 // 测试代码
ori a4, a4, 5 // 测试代码
sext.w a4, a4 // 测试代码
sw a4, 0(a5) // 测试代码
![RISCV TEST CONNECT](./imagesrc/riscv_test_switch.png)
addi sp, sp, -32 * REGBYTES
STORE sp, (a0)
</center>
STORE x1, 0 * REGBYTES(sp)
STORE x1, 1 * REGBYTES(sp)
csrr a0, mstatus
andi a0, a0, 8
beqz a0, save_mpie
li a0, 0x80
> save_mpie: ...
> #ifdef XS_USING_SMP ...
#endif
/* 将GPIO18 置为低电平 */
lui a5, 0x38001 // 测试代码
addi a5, a5, 12 // 测试代码
lw a5, 0(a5) // 测试代码
sext.w a4, a5 // 测试代码
lui a5, 0x38001 // 测试代码
addi a5, a5, 12 // 测试代码
addi a4, a4, -6 // 测试代码
sext.w a4, a4 // 测试代码
sw a4, 0(a5) // 测试代码
j xs_switch_kthread_context_exit
```
### 示波器测试选项设置
@ -791,7 +903,7 @@ XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext
* 反相:关闭
* 触发设置
* 类型:边沿
* 信源: CH1
* 信源 CH1
* 斜率:上升
* 模式:自动
* 触发电压:略低于最高电平即可
@ -810,7 +922,7 @@ XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext
</center>
从示波器测试结果上来看,测试SwitchKthreadContext的执行时间是160ns.
从示波器测试结果上来看,测试 SwitchKthreadContext 的执行时间是160ns。
<span id="comparison"></span>
@ -822,11 +934,11 @@ XiUOS的任务切换函数为xs_SwitchKthreadContext在SwitchKthreadContext
| XiUOS | KD233开发板 | RISC-V K210 主频 400MHz | 160 |
| XiUOS | STM32F407G-DISC1开发板 | STM32f407 主频 168MHz | 1260 |
结果分析:
* XiUOS在RISC-V K210 400MHz CPU主频上任务切换时间为 160 ns低于sylixos的 577.1 ns
* 若进行同等1GHz主频换算K210上的任务切换时间应为 62.5 nsXiUOS的任务切换的效率比sylixos提高 89.16%
结果分析
* XiUOS 在RISC-V K210 400MHz CPU主频上任务切换时间为 160 ns 低于 sylixos 的 577.1 ns
* 若进行同等1 GHz 主频换算K210 上的任务切换时间应为 62.5 nsXiUOS 的任务切换的效率比 sylixos 提高 89.16%
* 在ARM stm32f407 168MHz CPU主频任务切换时间 1260 ns高于1GHz主频测试的sylixos
* 若进行同等1GHz主频换算,STM32F407上的任务切换时间应为 206.718 nsXiUOS的任务切换的效率比sylixos提高 64.18%
* 若进行同等1 GHz 主频换算STM32F407 上的任务切换时间应为 206.718 nsXiUOS 的任务切换的效率比 sylixos 提高 64.18%
由于XiUOS优化了任务切换的流程减少了执行指令数因此同等主频条件下任务切换时间更短。