《FreeRTOS二》

四、任务调度方式
FreeRTOS支持三种调度方式:抢占式、时间片式、合作式调度实际上主要是抢占式和时间片式两种,合作式调度用的很少。
抢占式调度:
每个任务都有不同的优先级, 任务会一直运行直到被高优先级任务抢占或者遇到阻塞式的 API 函数,比如 vTaskDelay。

时间片调度:
每个任务都有相同的优先级, 任务会运行固定的时间片个数或者遇到阻塞式的 API 函数,比如vTaskDelay, 才会执行同优先级任务之间的任务切换。

五、临界段和开关中断
代码的临界段也称为临界区,一旦这部分代码开始执行,则不允许任何中断打断。为确保临界段代码的执行不被中断,在进入临界段之前须关中断,而临界段代码执行完毕后,要立即开中断。
FreeRTOS 的源码中有多处临界段的地方, 临界段虽然保护了关键代码的执行不被打断, 但也会影响系统的实时性。比如此时某个任务正在调用系统 API 函数,而且此时中断正好关闭了,也就是进入到了临界区中,这个时候如果有一个紧急的中断事件被触发,这个中断就不能得到及时执行,必须等到中断开启才可以得到执行, 如果关中断时间超过了紧急中断能够容忍的限度, 危害是可想而知的。
FreeRTOS 源码中就有多处临界段的处理, 跟 FreeRTOS 一样, uCOS-II 和 uCOS-III 源码中都是有临界段的, 而 RTX 的源码中不存在临界段。 另外, 除了 FreeRTOS 操作系统源码所带的临界段以外,用户写应用的时候也有临界段的问题,比如以下两种:
1、读取或者修改变量(特别是用于任务间通信的全局变量)的代码,一般来说这是最常见的临界代码。
2、调用公共函数的代码,特别是不可重入的函数,如果多个任务都访问这个函数,结果是可想而知的。总之, 对于临界段要做到执行时间越短越好, 否则会影响系统的实时性。
在FreeRTOS 任务代码中临界段的进入和退出主要是通过操作寄存器 basepri 实现的。进入临界段前操作寄存basepri 关闭了所有小于等于宏定义 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY所定义的中断优先级, 这样临界段代码就不会被中断干扰到, 而且实现任务切换功能的 PendSV 中断和滴答定时器中断是最低优先级中断, 所以此任务在执行临界段代码期间是不会被其它高优先级任务打断的。退出临界段时重新操作 basepri 寄存器,即打开被关闭的中断。
六、调度锁,任务锁和中断锁
调度锁:即任务调度器的开关。但是它并没有关闭任务的中断,意思是它只会保证任务不会被高优先级任务抢占,中断还是能够正常的执行。区分它和临界段的处理方式,调度锁只是禁止了任务调度, 并没有关闭任何中断,中断还是正常执行的。而临界段进行了开关中断操作。
任务锁:简单的说, 为了防止当前任务的执行被其它高优先级的任务打断而提供的锁机制就是任务锁。
中断锁:中断锁就是 RTOS 提供的开关中断函数, FreeRTOS 没有专门的中断锁函数,中断锁本质上来说就是使用临界代码段的关闭中断操作来执行。
总而言之、言而总之,这几种锁的目的都是人为的改变调度的顺序,或者是程序执行的顺序从而达到我们的目的。主要是明白可能影响系统执行结果的其他因素是来自中断还是其他任务就能借这些方式完成想要的功能。
七、时钟节拍
任何操作系统都需要提供一个时钟节拍,以供系统处理诸如延时、 超时等与时间相关的事件。时钟节拍是特定的周期性中断, 这个中断可以看做是系统心跳。 中断之间的时间间隔取决于不同的应用,一般是 1ms – 100ms。时钟的节拍中断使得内核可以将任务延迟若干个时钟节拍,以及当任务等待事件发生时,提供等待超时等依据。时钟节拍率越快,系统的额外开销就越大。一半系统节拍默认设置为1000Hz。
相关函数:
1、vTaskDelay用于任务延迟,高优先级任务调用此函数时释放CPU资源,供调度器安排给其他就绪态任务使用,而自身进入阻塞态。
2、vTaskDelayUntil同vTaskDelay一样,但是vTaskDelayUntil会多一个上次运行的时间参数参数,根据这个时间来提供延时,不会出现丢失任务的情况,详见vTaskDelayUntil与vTaskDelay区别
3、xTaskGetTickCount和xTaskGetTickCountFromISR用于获取当前运行的系统时钟节拍数,前者只能在任务里面调用,不能在中断当中调用,如果在中断里面的话要使用xTaskGetTickCountFromISR来获取节拍数。
八、事件标志组
事件标志组是实现多任务同步的有效机制之一。也许有不理解的初学者会问采用事件标志组多麻烦,搞个全局变量不是更简单? 其实不然, 在裸机编程时,使用全局变量的确比较方便,但是在加上 RTOS 后就是另一种情况了。 使用全局变量相比事件标志组主要有如下三个问题:
1、使用事件标志组可以让 RTOS 内核有效地管理任务, 而全局变量是无法做到的,任务的超时等机制需要用户自己去实现。
2、使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心。
3、使用事件标志组可以有效地解决中断服务程序和任务之间的同步问题。

事件标志通信过程:
任务Task1运行过程中调用函数xEventGroupWaitBits,等待事件标志位被设置,任务Task1由运行态进入到阻塞态。任务Task2设置Task1等待的事件标志,任务Task1由阻塞态进入到就绪态,在调度器的作用下由就绪态又进入到运行态。

事件标志组的中断方式:
任务Task1运行过程中调用函数xEventGroupWaitBits,等待事件标志位被设置,任务Task1由运行态进入到阻塞态。Task1阻塞的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中设置 Task1等待的事件标志,任务Task1由阻塞态进入到就绪态,在调度器的作用下由就绪态又进入到运行态。上面就是一个简单FreeRTOS中断方式事件标志通信过程。

事件标志组主要函数:
xEventGroupCreate()
xEventGroupCreateStatic()
vEventGroupDelete()
xEventGroupWaitBits()
xEventGroupSetBits()
xEventGroupSetBitsFromISR()
xEventGroupClearBits()
xEventGroupClearBitsFromISR()
xEventGroupGetBits()
xEventGroupGetBitsFromISR()
xEventGroupSync() 

说明:
xEventGroupWaitBits 如果由于指定的事件标志位被置1而返回,并且设置了这个函数的参数xClearOnExit为pdTRUE,那么此函数的返回值是清零前的事件标志组数值。

《FreeRTOS二》
http://example.com/2021/08/12/《FreeRTOS二》/
作者
HaoDuck
发布于
2021年8月12日
许可协议