FreeRTOS 资源管理介绍

本文探讨了多任务系统中资源访问的潜在风险,如非原子操作导致的数据损坏,以及互斥机制的重要性,包括临界区、互斥量和守护任务的使用,以确保数据一致性。同时提到了FreeRTOS中的相关解决方案和优先级反转的概念。
摘要由CSDN通过智能技术生成

多任务系统中存在一种潜在的风险。当一个任务在使用某个资源的过程中,即还没
有完全结束对资源的访问时,便被切出运行态,使得资源处于非一致,不完整的状态。
如果这个时候有另一个任务或者中断来访问这个资源,则会导致数据损坏或是其它相似
的错误。
以下便是一些例子:
1. 访问外设
考虑如下情形,有两个任务都试图往一个 LCD 中写数据:
 任务 A 运行,并往 LCD 写字符串”Hello world”。
 任务 A 被任务 B 抢占,但此时字符串才输出到”Hello w”。
 任务 B 往 LCD 写”Abort, Retry, Fail?”,然后进入阻塞态。
 任务 A 从被抢占处继续执行,完成剩余的字符输出——“orld”。
现在 LCD 显示的是被破坏了的字符串”Hello wAbort, Retry, Fail?orld”。

2. 读-改-写操作
程序清单 程序清单 57 展现的是一段 C 代码和其等效的 ARM7 汇编代码。可以看出,PORTA
中的值先从内存读到寄存器,在寄存器中完成修改,然后再写回内存。这所是所谓的读
-改-写操作。
/* The C code being compiled. */
155: PORTA |= 0x01;
/* The assembly code produced. */
0x00000264 481C LDR R0,[PC,#0x0070] ; Obtain the address of PORTA
0x00000266 6801 LDR R1,[R0,#0x00] ; Read the value of PORTA into R1
0x00000268 2201 MOV R2,#0x01 ; Move the absolute constant 1 into R2
0x0000026A 4311 ORR R1,R2 ; OR R1 (PORTA) with R2 (constant 1)
0x0000026C 6001 STR R1,[R0,#0x00] ; Store the new value back to PORTA
程序清单 程序清单 57 读- 改-写过程示例

这是一个”非原子”操作,因为完成整个操作需要不止一条指令,所以操作过程可能
被中断。考虑如下情形,两个任务都试图更新一个名为 PORTA 的内存映射寄存器:
 任务 A 把 PORTA 的值加载到寄存器中——整个流程的读操作。
 在任务 A 完成整个流程的改和写操作之前,被任务 B 抢占。
 任务 B 完整的执行了对 PORTA 的更新流程,然后进入阻塞态。
 任务 A 从被抢占处继续执行。其修改了一个 PORTA 的拷贝,这其实只是寄存
器在任务 A 回写到 PORTA 之前曾经保存过的值。
任务 A 更新并回写了一个过期的 PORTA 寄存器值。在任务 A 获得拷贝与更新回
写之间,任务 B 又修改了 PORTA 的值。而之后任务 A 对 PORTA 的回写操作,覆盖
了任务 B 对 PORTA 进行的修改结果,效果上等同于破坏了 PORTA 寄存器的值。
虽然是以一个外围设备寄存器为例,但是整个情形同样适用于全局变量的读-改-写
操作。
3. 变量的非原子访问
更新结构体的多个成员变量,或是更新的变量其长度超过了架构体系的自然长度
(比如,更新一个 16 位机上的 32 位变量)均是非原子操作的例子。如果这样的操作被中
断,将可能导致数据损坏或丢失。
4. 函数重入
如果一个函数可以安全地被多个任务调用,或是在任务与中断中均可调用,则这个
函数是可重入的。
每个任务都单独维护自己的栈空间及其自身在的内存寄存器组中的值。如果一个函
数除了访问自己栈空间上分配的数据或是内核寄存器中的数据外,不会访问其它任何
数据
如果一个函
数除了访问自己栈空间上分配的数据或是内核寄存器中的数据外,不会访问其它任何
数据,则这个函数就是可重入的。程序清单 58 示例了一个可重入函数,而程序清单 59
是一个不可重入函数的例子

/* A parameter is passed into the function. This will either be
passed on the stack or in a CPU register. Either way is safe as
each task maintains its own stack and its own set of register
values. */
long lAddOneHundered( long lVar1 )
{
/* This function scope variable will also be allocated to the stack
or a register, depending on compiler and optimization level. Each
task or interrupt that calls this function will have its own copy
of lVar2. */
long lVar2;
lVar2 = lVar1 + 100;
/* Most likely the return value will be placed in a CPU register,
although it too could be placed on the stack. */
return lVar2;
}
程序清单 程序清单 58 可重入函数示例
/* In this case lVar1 is a global variable so every task that calls
the function will be accessing the same single copy of the variable. */
long lVar1;
long lNonsenseFunction( void )
{
/* This variable is static so is not allocated on the stack. Each task
that calls the function will be accessing the same single copy of the
variable. */
static long lState = 0;
long lReturn;
switch( lState )
{
case 0 : lReturn = lVar1 + 10;
lState = 1;
break;
case 1 : lReturn = lVar1 + 20;
lState = 0;
break;
}
}
程序清单 程序清单 59 不可重入函数示例

互斥 互斥
访问一个被多任务共享,或是被任务与中断共享的资源时,需要采用”互斥”技术以
保证数据在任何时候都保持一致性。这样做的目的是要确保任务从开始访问资源就具有
排它性,直至这个资源又恢复到完整状态。
FreeRTOS 提供了多种特性用以实现互斥,但是最好的互斥方法(如果可能的话,
任何时候都当如此)还是通过精心设计应用程序,尽量不要共享资源,或者是每个资源
都通过单任务访问。
本章期望让读者了解以下内容:
 为什么,以及在什么时候有必要进行资源管理与控制。
 什么是临界区。
 互斥是什么意思。
 挂起调度器有什么意义。
 如何使用互斥量。
 如何创建与使用守护任务。
 什么是优先级反转,以及优先级继承是如何减小(但不是消除)其影响的。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶知秋yyds

分享是一种美德,感谢金主打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值