IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【FreeRTOS】06 临界段的保护——关中断和关调度 -> 正文阅读

[嵌入式]【FreeRTOS】06 临界段的保护——关中断和关调度

本节来讲一讲FreeRTOS如何保护临界段,先讲临界段的概念,再讲保护临界段的方法。

1)临界段的概念

简单来讲,临界段是一段执行时不允许被中断(或其他任务)打断的代码;如果被打断,就有可能运行出错。

举个例子:

假设我们有一个任务Task1需要向队列FIFO中写入数据,中断服务函数TaskISR1也需要向队列FIFO中写入数据;我们知道,FIFO的写或者读需要依次执行:写入/读出数据、更新读/写指针。

如果Task1在向FIFO中写入数据时,执行完写入数据,但还没有更新写指针,此时中断来了,TaskISR1开始运行,它也需要向FIFO中写入,而由于Task1运行时还没有更新写指针,则TaskISR写入的数据会把Task1刚刚写入的数据覆盖,然后更新写指针;这就出现了一次丢失数据的错误。

当然错误还不只如此,中断服务程序TaskISR1运行完毕后,回到Task1继续运行,它会接着被打断的位置向后执行,更新写指针;这样就相当于没有写入数据,但又更新了一次写指针。

像这个例子中的FIFO写入数据、读出数据的代码段,就是临界段;它的读写数据和更新读写指针,两个操作之间不能被打断,否则就会可能出错。

用户代码中用于任务和中断间通信的变量、不可重入函数,操作系统的某些底层代码,都有可能是临界段。如果不保护好临界段,代码就会出现各种难以排查、难以复现的bug。

最简单的保护临界段的方法,就是关闭中断,以及关闭任务调度。也就是暂时禁止中断和其他任务执行,等到临界段执行完毕后再恢复。具体使用时,如果有可能被中断打断,则关闭中断,如果有可能被高优先级的任务打断,则可以关闭任务调度。

注意!这种“关闭”是临时的,执行完临界段之后,必须要恢复允许中断和任务调度的状态。否则,中断和其他任务就无法运行了。

此外,由于执行临界段时,关闭了任务或任务调度,系统无法响应其他的实时事件,会降低系统的实时性;所以我们编写代码时,需要准确识别临界段,使得临界段尽量短,即只把需要保护的最小的一段代码保护起来即可。

2)freeRTOS中的临界段保护

freeRTOS系统中定义了保护临界段的宏:

在任务中,taskENTER_CRITICAL(),用于进入临界段前保护;taskEXIT_CRITICAL(),用于临界段执行完之后,退出保护。

查看代码调用情况,可以看到它们最后是由下述两个函数实现:

这其中除了开/关中断的代码,还有一个嵌套计数,为的是记录进入了几层“关中断”中,这样在退出临界区时,就不会错误地提前退出了。

其中的开/关中断的操作,是用嵌入的汇编代码实现的,如下所示,关闭中断时,只关闭了优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断:

由于freeRTOS提供的进入临界段的代码,本质上是关闭中断,而一旦关闭中断,则freeRTOS的任务切换功能也被禁止了,所以,它同时能够保护中断和任务间的临界段冲突

另外,如果想要在中断服务程序中进入临界段,要使用另一套函数来保护临界段:taskENTER_CRITICAL_FROM_ISR(),用于进入临界段前保护;taskEXIT_CRITICAL_FROM_ISR( x )用于临界段执行完之后,退出保护。代码的实现是类似的,感兴趣的可以自行阅读代码。

3)保护临界段的测试实例

我们以一个例子来试验freeRTOS临界段的保护功能:

首先来看不保护临界段的情况。

建立任务,我们把defaultTask任务优先级设置为普通,Task02任务的优先级设置为高,这样,Task02就可以打断defaultTask的执行:

编写一个具有临界段属性的函数,这里我们用串口来实现,就是普通地往串口寄存器中写入数据:

编写两个任务内部执行的功能,都是通过访问串口的临界段函数,打印输出;不同的是高优先级的Task2第一次运行时延迟了5ms,这样可以实现defaultTask任务先执行5ms,之后Task02打断它执行:

上述程序运行结果如下:

可以看到,串口打印的输出,DefaultTask任务首次运行时,打印了前6个字符:Defaul后,被Task02打断,进入了Task02任务的打印功能;Task02打印完毕后,才继续打印DefaultTask未完成的打印字符:tTask is running。

所以,如果临界段Uart1Send函数不加保护,它的打印输出有可能被打断,输出不正确的字符串。

如下所示修改defaultTask的代码,加入临界段保护和退出的语句:

再次运行上述程序,可以看到输出如下,输出了正确的字符串:

所以,临界段保护代码功能是有效的。

4)关闭任务调度

上述的freeRTOS定义的临界区保护的操作,是直接关闭中断;而实际上,有时有的临界段中断里不访问,只用保护不被其他任务打断即可;这时也可以临时关闭任务调度功能,来实现临界段的保护。

freeRTOS系统中定义了关闭任务调度和开启任务调度的函数:

vTaskSuspendAll()、xTaskResumeAll()

这两个函数也需要成对使用,关闭任务调度后,然后执行临界段,最后一定要恢复到开启调度的状态。

而且,在关闭调度后,不能再使用任何会引起任务切换的函数,如之前讲到的延时vTaskDelayUntil、vTaskDelay,以及以后会讲到的信号量、消息队列等函数;直到重新开启任务调度。

在3)中的例子,临界段访问的冲突是不同任务间引起的,我们也可以通过临时关闭任务调度来实现临界段的保护,如下所示:

这个程序的执行结果,和使用taskENTER_CRITICAL()、taskEXIT_CRITICAL()保护临界段是一样的。大家可以自行测试一下。

好了,本节的内容就到这里了。

如果觉得有用可以关注作者微信号“小白白学电子”,在公众号可以找到代码和资料下载地址:

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-06-20 23:06:12  更:2022-06-20 23:06:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:42:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码