杨为民 发表于 2023-8-29 13:08:50

熊仔 发表于 2023-8-29 12:23
我的解决方案:先对CriticalNesting判断是否为0,这样肯定是第一次进入。
这样就防止抬杠做法。



“当然用户不能修改系统函数,修改了就是更加抬杠了。”
请问用户修改了是谁在更加抬杠了?我?你?用户?


CosyOS 发表于 2023-8-29 13:28:57

我的理解,JB指令确实不能替代JBC,原因是:
JB   EA, Lab

Lab:CLREA
如果使用了JB指令,当JB指令执行时假定EA为1(下一步本应跳转到Lab处),而后进入了中断服务程序,而在中断服务程序中又关闭了中断EA = 0;
当返回后,EA的原状态应为0,而不是1,也就是说不应该跳转到Lab处执行,而应该是继JB指令后顺序执行。
如果使用了JBC指令,当JBC指令执行时假定EA为1,则跳转到Lab处并清EA,此过程不会被中断服务程序打断,EA的原状态不会改变始终为1,实现了原子操作。




杨为民 发表于 2023-8-29 13:30:57

熊仔 发表于 2023-8-29 11:21
杨老师实现的方法和我实现的方法思路基本是一致的。




这个情况会引起系统不良吗?我认为不会的。即便是发生中断,执行对应的中断服务函数,然后返回还是这个位置,下一步执行CLREA。中断产生是不能预料的。EA清0之前,都有可能发生。如果太纠结的话,程序刚好执行LCALL OS_ENTER_CRITICAL() 跳转到临界区函数是否有可能产生硬件中断?即便是发生中断,执行对应的中断服务函数,然后返回还是这个位置,下一步执行JBC 语句。
对于上面说的2种可能发生的中断,都对系统没有造成不良影响。系统不会崩溃。(1)你说的这些只是针对你自己写的只有两个定时器中断的RTOS程序得出的结论,怎么能代表普遍的情况呢?你移植的RTOS是只给你自己用吗?只能有定时器中断吗?如果是,那么你的结论是正确的,如果你的RTOS是要提供给STC用户使用,你这种结论就太以偏盖全了!(2)我建议你在对原子指令的作用进行评价之前,先读读相关的书,了解一下加锁不牢产生的严重后果,了解一下和动脑筋想一下为什么自8051开始的CPU,包括现在的ARM、Cortex和RISC-V等CPU都有为加锁解锁定制的“原子指令”,这些指令集的设计者会笨到设计一些无用的指令吗?

CosyOS 发表于 2023-8-29 13:52:07

在正常情况下是不会有人在中断中关闭EA,但也不能排除会有这样的特殊需求。
原子操作的意义在于不可再分、不会被打断,确保万无一失。

杨为民 发表于 2023-8-29 14:16:05

CosyOS 发表于 2023-8-29 13:28
我的理解,JB指令确实不能替代JBC,原因是:
JB   EA, Lab



{:4_250:}理解正确。

(1)加锁解锁过程首先读锁的标志,然后根据锁的标志判断执行“if ... else ....”的二选一分支执行。如果读标志之后没有清除标志,则程序被中断后被另外的优先级更高的任务根据锁标志执行临界区操作,并清了标志,中断回来后原程序已经在“if”的分支里了,会继续地执行临界区操作,就有可能产生问题。
(2)举个例子,规则是小孩带帽子就是要洗澡了,然后爸爸看见小带帽子了,还没有来得及摘帽子,妈妈进来就给小孩洗澡摘帽子了,然后轮到爸爸了,爸爸已经看过小孩带帽子的样子了,所以接着给小孩洗澡摘帽子。最终小孩洗了两次澡。
洗两次澡问题不大,吃两次饭呢?在生产上往罐头里装两次料呢,烧饼被烤了两次呢?原料被加工了两次呢?这种例子问题生活生产上比比皆是,所以操作系统原理专门讲解软件加锁解锁方法原理,硬件工程师专门设计CPU指令来保证加锁牢靠(俗称给加锁程序上锁),而我们程序员要保证JBC指令放在加锁程序的第一条位置。
(3)对于熊仔的方法4:
void OSEnterCritical(void)
{
    if(CriticalNesting==0)
    {
      if(_testbit_(EA))      //相当于 JBC bitvar 测试该位变量并跳转同时清除
      {
            _bEA = 1;
      }else{
            _bEA = 0;
      }
    }
    //EA = 0;    //加上更加保守做法,每次都清0
    CriticalNesting++;
}

汇编出来头几行为:
             ; FUNCTION OSEnterCritical (BEGIN)
                                           ; SOURCE LINE # 410
                                           ; SOURCE LINE # 411
                                           ; SOURCE LINE # 412
0000 E500      R   MOV   A,CriticalNesting
0002 700B            JNZ   ?C0012
                                           ; SOURCE LINE # 413
                                           ; SOURCE LINE # 414
0004 10AF02            JBC   EA,?C0024
0007 8004            SJMP    ?C0013
0009         ?C0024:
即使一开始用了“CriticalNesting”作为加锁标志,但是头两行程序

0000 E500      R   MOV   A,CriticalNesting
0002 700B            JNZ   ?C0012

不是“原子指令”,所以在JNZ指令执行前仍然存被中断的可能,因此熊仔的这个版本仍然存在加锁不牢的BUG。
(4)其实消除这个BUG很简单,只要调一下C语言的次序就行了:

void OSEnterCritical(void){
      if(_testbit_(EA))      //相当于 JBC bitvar 测试该位变量并跳转同时清除

      {
         if(CriticalNesting==0)    {
            _bEA = 1;
          }
      }else{
         if(CriticalNesting==0)    {
            _bEA = 0;
          }
      }
    //EA = 0;    //加上更加保守做法,每次都清0

    CriticalNesting++;
}





CosyOS 发表于 2023-8-29 14:44:00

通过杨老师的讲解,我对“原子操作”及“加锁/解锁”的理解更为透彻了,也认识到了加锁不牢的严重性,感谢杨老师。{:4_196:}

CosyOS 发表于 2023-8-29 14:44:00

通过杨老师的讲解,我对“原子操作”及“加锁/解锁”的理解更为透彻了,也认识到了加锁不牢的严重性,感谢杨老师。{:4_196:}

杨为民 发表于 2023-8-29 15:01:59

CosyOS 发表于 2023-8-29 14:44
通过杨老师的讲解,我对“原子操作”及“加锁/解锁”的理解更为透彻了,也认识到了加锁不牢的严重性,感谢 ...

其实后果真的会很严重,比如火车或者动车的一个安全区间里放进了两辆车,如果是对头的就撞了,如果是同向的且后车速度快,就追尾了。很多年前国内最严重的动车事故就是好几道锁都没有加牢,一个区间里放进两辆车造成的,而且查明了是软件漏洞

熊仔 发表于 2023-8-29 15:02:10

本帖最后由 熊仔 于 2023-8-29 15:18 编辑

其实在临界区不慎操作EA=1;不成对使用临界区方法,在中断里面关闭中断开关,返回的时候忘记开启。这些都是程序员写代码的bug。
我们考虑他们自己的bug,感觉有点过头了。

只能说给他们上个保险。
最终问题解决还是靠写代码的人或者检查的人发现漏洞。

熊仔 发表于 2023-8-29 18:29:43

本帖最后由 熊仔 于 2023-8-29 18:51 编辑

杨老师请您点评下之前弄的方法4,也就是freeRTOS的嵌套方法


void OSEnterCritical(void)
{
    EA = 0;
    CriticalNesting++;
}


void OSEXitCritical(void)
{
    CriticalNesting--;
    if (CriticalNesting == 0){
      EA = 1;
    }
}



这个方法,我在调试发现,执行OSTaskCreate后,自动开启了中断。因为系统没有启动,退出临界区的时候EA = 1;
目前也就发现这个瑕疵,其实也不会引起严重问题,系统没有开始调度。


貌似系统开始调度后,想不到有什么bug。


进入临界区,粗暴的关中断。后面的操作看起来没有啥问题。


想到一个,也就是用户使用不当导致的,没有成对调用。没有判断CriticalNesting>0
其实我不同意这个是bug。
程序员引起的问题。不遵守规则。


页: 1 [2]
查看完整版本: uC/OS-II移植记(9):微山x51-uCOSII中的可嵌套临界区保护方法