本帖最后由 杨为民 于 2023-8-24 20:35 编辑
临界区保护方法2和方法3存在BUG 楼主推出的“uCOSII-STC8-V1.01-20230823”移植版中,除了临界区保护不能嵌套的方法1外,还给出了临界区保护可以嵌套的方法2和方法3。为了验证这两种方法是否可以嵌套使用,笔者专门编程了一个验证程序。对于这个验证程序,这个移植版的方法2和方法3均没有达到能够嵌套保护的目的,说明这个移植版的临界区保护方法2和方法3程序存在BUG。 一、临界区保护嵌套是否有效的检测程序
(1)验证是不是在临界区保护中的简单方法是观测程序的定时器中断信号,如果处于临界区保护中,则定时器中断被禁止,如果处于临界区保护外,则定时器中断正常发生。下面是本范例中断ISR的程序: 其中第88行和第97行是一对,每次定时器1中断在逻辑分析仪通道0产生一个正脉冲。第106行和第113行是一对,每次定时器3中断在逻辑分析仪通道6产生一个正脉冲。 因此只要观测通道0和通道6就可以知道是否处于临界区保护之下了。
(2)验证程序仍然使用三个实时任务,主要用连接在P2端口的逻辑分析仪来观测效果。其中优先级最高的任务A程序如下: 这是一个P1端口LED灯左右两边交替闪烁的程序,每个主循环闪灯3次,然后就将任务自己挂起。在任务A获得控制权期间,逻辑分析仪的第4通道显示10毫秒周期的方波信号。
(3)任务B的程序如下: 这是一个在P3端口实习跑马灯的程序,每个主循环跑一次,然后就将任务自己挂起。在任务B获得控制权期间,逻辑分析仪的第5通道显示10毫秒周期的方波信号。
(4)任务C是本验证范例的主控程序,由它来验证临界区保护嵌套的情况。验证每个主循环为一个周期,如果临界区保护可以嵌套,则对应的逻辑分析仪的信号如下图,具体的信号在后面结合程序解释: 图1 逻辑分析仪信号图
(5)任务C主循环的第一个部分的程序如下: 其中第201到207行时每个验证周期的起始信号,在P2端口的所有通道上产生一个10毫秒的正脉冲,见上面“图1 逻辑分析仪信号图 ”的开始部分。 第210行到215行P0端口的LED灯左右闪烁,并只在通道2上产生一个100毫秒的正脉冲。由于这个时候没有进入临界区保护,从图1上可以看到通道0和通道6都有连续的中断信号。 (6)任务C从第218行开始进入临界区保护,对应图1中的小旗子1,从这个时候开始通道0和通道6都没有有中断信号,说明确实已经进入了临界区保护。然后第221行到233行,任务C在通道2上产生30毫秒周期方波信号。然后237行唤醒挂起的任务A。在通道5上产生了方波信号。由于这时系统处于一层临界区保护之下,所以在整个任务A执行期间通道0和通道6都没有定时器中断信号产生。
(7)下面是任务C剩余的程序: 由于任务A的优先级比任务C高,所以在任务A运行时任务C被挂起,直到任务A执行完挂起后,任务C开始执行第240行,临界区保护进入第二层嵌套保护。在图1中从第2面小旗处开始。 (8)任务C的第243行到251行程序在P0端口上产生一个跑马灯,在通道2上产生10毫秒的方波,见图1紧跟第2面小旗的部分。 然后第265行程序使得任务B就绪,由于任务B的优先级3仍然高于任务C的优先级4,所以控制权交给任务B执行,在通道5上产生方波信号。由于这时系统处于二层临界区保护之下,所以在整个任务B执行期间通道0和通道6都没有定时器中断信号产生。 (9)任务C最后第255行到第265行程序依次退出二层临界区保护,转到主循环开始一轮新的检测。 二、对楼主移植版临界区保护方法的检测 (10)楼主移植版临界区保护方法的程序如下: /***************************************** 与处理器有关的代码*****************************************/ //注意OS_CPU_A.A51文件也需要设置下OS_CRITICAL_METHOD选哪种一种方法 ,需要保持两边一致。 // 方法1 #define OS_CRITICAL_METHOD 1 #if OS_CRITICAL_METHOD==1 //但这样有一个问题,如果禁止中断的情况下调用OS功能函数,那么从功能函数返回时,中断可能变成允许的了,而实际上还是希望是禁止的。 #define OS_ENTER_CRITICAL() EA=0/* 直接禁止中断 */ #define OS_EXIT_CRITICAL() EA=1/* 直接允许中断 */ #endif #if OS_CRITICAL_METHOD==2 //执行OS_ENTER_CRITICAL()时,先将中断状态保存到堆栈,然后关中断; //执行OS_EXIT_CRITICAL()时,再从堆栈中恢复原来的中断开/关状态。这种方法不会改变中断状态,避免前面的问题。 //但是,当用户使用的处理器有堆栈指针相对寻址模式时,可能出现严重错误。 //问题分析:OS_ENTER_CRITICAL()时导致中断切换任务的时候硬件堆栈多入了一个_push_(IE), //解决方法:OSIntCtxSw()函数调整SP的时候应该多减1,原来SP=SP-4改为SP=SP-5 #include <intrins.h> #define OS_ENTER_CRITICAL()do{_push_(IE);EA = 0;}while(0) /* 利用堆栈保存中断状态,再关中断 */ #define OS_EXIT_CRITICAL()do{ _pop_ (IE);}while(0) /* 将IE从堆栈弹出,恢复IE值 */ #endif #if OS_CRITICAL_METHOD==3 //获取当前中断状态的值,并将其保存在C函数局部变量之中 //问题分析:可重入函数的局部变量cpu_sr会入仿真栈。导致中断切换任务的时候,仿真堆栈出问题。 //解决方法:OSIntCtxSw()函数执行时需要对仿真堆栈指针++操作。 #define OS_ENTER_CRITICAL() do{ cpu_sr=IE;EA = 0;}while(0) #define OS_EXIT_CRITICAL() do{ IE = cpu_sr; }while(0) #endif
(11)对楼主移植版临界区保护选择方法1的检测结果如下: 如楼主移植版说明中的那样,在执行任务C的第237行和第255行程序“调用OS功能函数,那么从功能函数返回时,中断可能变成允许的了”,所以在任务A和任务B执行期间,通道0和通道6的定时器中断信号仍然存在。 由于临界区保护方法1本身就不具备嵌套功能,所以上面这个结果对方法1是正确的,移植版的方法1没有BUG。
(12)对楼主移植版临界区保护选择方法2和方法3的检测结果如下: 从图中可以看出方法2和方法3的结果与方法1一样,并没有像图1一样具有临界区保护嵌套功能,所以楼主移植版的临界区保护方法2和方法3没有实现嵌套保护功能,肯定存在BUG。
下面是验证程序:
uCOSII_临界区保护_方法1.rar
(681.32 KB, 下载次数: 118)
uCOSII_临界区保护_方法2.rar
(683.37 KB, 下载次数: 114)
uCOSII_临界区保护_方法3.rar
(684.16 KB, 下载次数: 117)
|