2021SC@SDUSC
一. 调度器
调度器就是使用相关的调度算法来决定当前需要执行进程的程序模块。所有的调度器都有一个共同的特性:调度器可以区分就绪态进程和挂起进程。调度器可以选择所有就绪态进程中的一个,然后通过执行它来激活这个进程。不同的调度器之间的区别就在于调度算法不同。从底层看,调度器实则是一个多个不同进程共享的定时器中断服务程序。
嵌入式实时操作系统的核心就是调度器和进程切换
1. 合作式调度器
合作式调度器就是根据用户的设置时刻或者周期来执行相应的进程,这些进程执行期间不支持被抢占,也就是说不允许在进程执行期间被抢占CPU,只允许进程自愿放弃CPU的控制权
2. 抢占式调度器
在实际应用中,因为不同的进程需要不同的响应时间,所以当一个进程所需执行时间长而又不紧急的进程排在一个所需执行时间短而又紧急的进程之前时,紧急的进程就无法被及时的执行,导致效率的降低,此时抢占式调度器就是必须的。**如果采用抢占式调度:**当一个比当前进程优先级高的进程进入了就绪态,当前执行的进程就会被剥夺CPU控制权,并交给优先级更高的进程。
使用抢占式调度器,最重要的优点就是进程的响应时间得以优化,在下面我们即将要说到的TencentOS Tiny的调度器在不同优先级的进程之间也是采用抢占式调度器
3. 时间片调度器
在小型的嵌入式RTOS中,最常用的时间片调度算法就是Round-robin调度算法,这种调度算法可以用于抢占式或合作式的多进程中,时间片调度适合用于不要求进程实时响应的场景下。
实现Round-robin调度算法需要给同优先级的进程分配一个专门的列表,用于记录当前就绪的进程,并为每个进程分配一个时间片,时间片就是进程一次在CPU上执行的时间,并且进程的完成需要在CPU上执行多次,当进程在一次执行中用完时间片就会将CPU控制权交付给下一个进程。
目前的embOS,FreeRTOS,μCOS-III 和 RTX 都支持 Round-robin 调度算法。
二. TencentOS Tiny的调度器
在TencentOS Tiny中,调度器的使用分为两种情况:
- 针对不同优先级进程之间的调度:调度器采用的是基于优先级的全抢占式调度,在系统运行过程中,当有比当前进程优先级更高的就绪时,当前进程将立刻被切出,高优先级进程抢占处理器运行
- 针对相同优先级进程之间的调度:调度器采用的是时间片轮转方式进行调度
下面我们通过源码来分析一下 TencentOS Tiny的调度器工作流程:
启动调度器
调度器的启动由cpu_sched_start函数来完成,它会被tos_knl_start函数调用,这个函数中主要做两件事,首先通过readyqueue_highest_ready_task_get函数获取当前系统中处于最高优先级的就绪进程,并且将它赋值给指向当前进程控制块的指针k_curr_task,然后设置一下系统的状态为运行态KNL_STATE_RUNNING。
函数cpu_sched_start是用汇编代码写的,由于TencentOS Tiny支持多种内核的芯片,分别有M3/M4/M7等,不同芯片对于函数cpu_sched_start的实现是不同的,在这里以M4为例:
__API__ k_err_t tos_knl_start(void)
{
if (tos_knl_is_running()) {
return K_ERR_KNL_RUNNING;
}
k_next_task = readyqueue_highest_ready_task_get();
k_curr_task = k_next_task;
k_knl_state = KNL_STATE_RUNNING;
cpu_sched_start();
return K_ERR_NONE;
}
在上面这个函数中,readyqueue_highest_ready_task_get()函数的结果是获取到目前已就绪进程中优先级最高的进程,然后返回给k_next_task,并且将其赋值到当前进程块的指针k_curr_task,修改内核运行状态为运行态
port_sched_start
CPSID I
; set pend