找回密码
 立即注册
楼主: 杨***

uC/OS-II移植记(9):微山x51-uCOSII中的可嵌套临界区保护方法

[复制链接]

该用户从未签到

63

主题

703

回帖

1万

积分

荣誉版主

积分
10906
 楼主| 发表于 2023-8-29 13:08:50 | 显示全部楼层
熊仔 发表于 2023-8-29 12:23
我的解决方案:先对CriticalNesting判断是否为0,这样肯定是第一次进入。
这样就防止抬杠做法。

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


点评

我在抬杠,😄。 杨老师提出的原子操作指令,这个受益匪浅。一步一步按照杨老师的提示去优化。 非常感谢杨老师的指点。  发表于 2023-8-29 13:29
回复 支持 反对 送花

使用道具 举报

  • TA的每日心情
    奋斗
    昨天 11:08
  • 签到天数: 173 天

    [LV.7]常住居民III

    5

    主题

    579

    回帖

    2347

    积分

    荣誉版主

    积分
    2347
    发表于 2023-8-29 13:28:57 | 显示全部楼层
    我的理解,JB指令确实不能替代JBC,原因是:
    JB     EA, Lab

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




    点评

    这个理解不正确。 正常的程序员在中断退出前开中断的。 其实我们还是停留在裸机开发单片机,认为中断是可以返回的。 在RTOS,中断是可以不返回的。 这也是我之前深陷的一个误区,发表的谬论。 主要原因就是高优先   发表于 2023-8-29 22:58
    理解正确。 (1)加锁解锁过程首先读锁的标志,然后根据锁的标志判断执行“if ... else ....”的二选一分支执行。如果读标志之后没有清除标志,则程序被中断后被另外的优先级更高的任务根据锁标志执行临界  详情 回复 发表于 2023-8-29 14:16
    我理解的就是实时响应的问题。就是所谓的指哪打哪。 想执行下面那一条指令。这时候必须要执行,不能中断。 只要中断执行够快实际上没多大影响。起码系统不会崩溃。   发表于 2023-8-29 13:35
    在中断里面关闭EA,然后退出中断。 这种也是抬杠的做法。哈哈哈  发表于 2023-8-29 13:30
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    63

    主题

    703

    回帖

    1万

    积分

    荣誉版主

    积分
    10906
     楼主| 发表于 2023-8-29 13:30:57 | 显示全部楼层
    熊仔 发表于 2023-8-29 11:21
    杨老师实现的方法和我实现的方法思路基本是一致的。

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

    点评

    之前停留在裸机开发,认为中断会返回的。 其实RTOS的中断可以不返回到断点,切换到就绪的任务。 感谢杨老师指点迷津  发表于 2023-8-29 23:19
    感谢杨老师指点,好好去学习一下。  发表于 2023-8-29 13:42
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 11:08
  • 签到天数: 173 天

    [LV.7]常住居民III

    5

    主题

    579

    回帖

    2347

    积分

    荣誉版主

    积分
    2347
    发表于 2023-8-29 13:52:07 | 显示全部楼层
    在正常情况下是不会有人在中断中关闭EA,但也不能排除会有这样的特殊需求。
    原子操作的意义在于不可再分、不会被打断,确保万无一失。
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    63

    主题

    703

    回帖

    1万

    积分

    荣誉版主

    积分
    10906
     楼主| 发表于 2023-8-29 14:16:05 | 显示全部楼层
    CosyOS 发表于 2023-8-29 13:28
    我的理解,JB指令确实不能替代JBC,原因是:
    JB     EA, Lab

    理解正确。

    (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++;
    }





    点评

    是的,应该这么做。早上看到杨老师的版本,然后修改了,已经公布了。 赋值_bEA赋值之前一定要判断是否在第1层。解决我提出的在嵌套内不慎操作操作EA=1的问题。   发表于 2023-8-29 14:46
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 11:08
  • 签到天数: 173 天

    [LV.7]常住居民III

    5

    主题

    579

    回帖

    2347

    积分

    荣誉版主

    积分
    2347
    发表于 2023-8-29 14:44:00 | 显示全部楼层
    通过杨老师的讲解,我对“原子操作”及“加锁/解锁”的理解更为透彻了,也认识到了加锁不牢的严重性,感谢杨老师。

    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 11:08
  • 签到天数: 173 天

    [LV.7]常住居民III

    5

    主题

    579

    回帖

    2347

    积分

    荣誉版主

    积分
    2347
    发表于 2023-8-29 14:44:00 | 显示全部楼层
    通过杨老师的讲解,我对“原子操作”及“加锁/解锁”的理解更为透彻了,也认识到了加锁不牢的严重性,感谢杨老师。

    点评

    其实后果真的会很严重,比如火车或者动车的一个安全区间里放进了两辆车,如果是对头的就撞了,如果是同向的且后车速度快,就追尾了。很多年前国内最严重的动车事故就是好几道锁都没有加牢,一个区间里放进两辆车造成  详情 回复 发表于 2023-8-29 15:01
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    63

    主题

    703

    回帖

    1万

    积分

    荣誉版主

    积分
    10906
     楼主| 发表于 2023-8-29 15:01:59 | 显示全部楼层
    CosyOS 发表于 2023-8-29 14:44
    通过杨老师的讲解,我对“原子操作”及“加锁/解锁”的理解更为透彻了,也认识到了加锁不牢的严重性,感谢 ...

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

    使用道具 举报

    该用户从未签到

    11

    主题

    331

    回帖

    886

    积分

    荣誉版主

    积分
    886
    发表于 2023-8-29 15:02:10 | 显示全部楼层
    本帖最后由 熊仔 于 2023-8-29 15:18 编辑

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

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

    使用道具 举报

    该用户从未签到

    11

    主题

    331

    回帖

    886

    积分

    荣誉版主

    积分
    886
    发表于 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。
    程序员引起的问题。不遵守规则。


    回复 支持 反对 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-5-18 02:44 , Processed in 0.080914 second(s), 71 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表