打印

ucos临界段 疑惑 求教!

[复制链接]
4175|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
电子乌托邦|  楼主 | 2011-2-24 10:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我知道的:
临界段通过关中断的方式,禁止了任务调度,虽然降低了实时性,但保护了全局变量不被修改。

解释一下:
int a;// 全局变量

void taska(void)                     void taskb(void)
{                                          {
     while(1)                                 while(1)
     {                                          {
           a = 5;       // 1                        a = 8;       // 3
           a++;         // 2                        .....
           .........                             }
     }                                     }
}

当程序运行到1句时,a = 5,此时被高优先级任务b抢占,全局变量a=8;当回到任务a时,a++就变成9了,一般不是用户所希望的。
所以taska中的全局变量应该保护一下,方法有很多,可以用开关中断的方法,于是改成如下任务:

void taska(void)                     
{                                          
     while(1)                                
     {                                         
         OS_ENTER_CRITICAL();
          a = 5;       // 1               
           a++;         // 2               
         OS_EXIT_CRITICAL();
            .........                        
     }                                    
}
这样修改a的时就不会被其他任务修改;这是容易理解的。

说了这么多,进入正题:
引用ucos内核os_core.c文件的一句程序

OS_ENTER_CRITICAL();
OSIdleCtr    = 0L;               /* Clear idle counter */
OS_EXIT_CRITICAL();

疑问:为何赋值0也要保护,就算赋值前OSIdleCtr被修改,但是再回来还是会被赋值0,所以我认为此处的临界段处理是多余的。请问诸位我理解对吗?

相关帖子

沙发
ayb_ice| | 2011-2-24 10:54 | 只看该作者
LZ只考虑了A-->B,难道没有B-->A吗

使用特权

评论回复
板凳
电子乌托邦|  楼主 | 2011-2-24 11:21 | 只看该作者
谢谢ayb_ice回复,
能再详细一点说吗!

使用特权

评论回复
地板
电子乌托邦|  楼主 | 2011-2-24 11:24 | 只看该作者
我举个例子
void taska(void)                     
{                                          
     while(1)                                
     {                                         
         OS_ENTER_CRITICAL();
          a = 5;       // 1               
           a++;         // 2               
         OS_EXIT_CRITICAL();
            .........                        
     }                                    
}
void taskb(void)
{
while(1)
{
    a = 8;       // 3
     ... ..
}
}

任务b中没有临界保护,有什么影响?

使用特权

评论回复
5
new1988| | 2011-2-24 11:33 | 只看该作者
本帖最后由 new1988 于 2011-2-24 11:37 编辑

你就确定B是最高优先级的,系统中就没有比b更高的优先级的任务啦?就算B最高,但是还有中断函数啊,要是你在中断中引用了这个变量呢?

使用特权

评论回复
6
电子乌托邦|  楼主 | 2011-2-24 11:36 | 只看该作者
你就确定B是最高优先级的,系统中就没有比b更高的优先级的任务啦?
new1988 发表于 2011-2-24 11:33

当然有,抢占了,回来后,a还是赋值8,而不会被赋值其他,所以认为临界处理是多余。

使用特权

评论回复
7
ayb_ice| | 2011-2-24 11:42 | 只看该作者
一个基本原则,全局变量的操作必须保护,除非能确定不被多个任务或中断共享

使用特权

评论回复
8
new1988| | 2011-2-24 11:58 | 只看该作者
LS已经说明白了,这是OS的基本原则。OS的作者他怎么知道OSIdleCtr这个全局变量会不会被用户放到中断里面去或者放到更高优先级别的任务里面去啊!

使用特权

评论回复
9
电子乌托邦|  楼主 | 2011-2-24 12:05 | 只看该作者
谢谢楼上回复
我的意思是:
我认为a++应该受到保护,单纯的一句a=8,则没必要保护,因为a是一个常量赋值。是不会因为任务中断而改变的。

使用特权

评论回复
10
电子乌托邦|  楼主 | 2011-2-24 12:08 | 只看该作者
还有我认为不是所有的全局变量都要保护,有些定义了大数组,不能放到局部变量里,所以用的全局变量,但是只有而且唯一一个任务使用,中断中也没有使用,这样的全局变量,我认为可以当做局部变量来看待。也不需要保护。
我是对这种常量赋值变量的赋值感觉多余。
请大家指教

使用特权

评论回复
11
john_lee| | 2011-2-24 12:20 | 只看该作者
LZ的想法原则上是正确的。直接赋值是不需要保护的!但有一个附加条件:赋值操作必须以单个CPU指令完成。
例如:OSIdleCtr的宽度是32位,ARM CPU只需一条指令就可以赋值,而51,AVR等8位CPU必须分4次才能完成赋值,为了保证原子操作,就需要对其进行临界保护。

使用特权

评论回复
12
电子乌托邦|  楼主 | 2011-2-24 12:33 | 只看该作者
谢谢楼上回复,
我的疑问正是在于此,
我认为虽然是分4条指令完成,但都是调用的寄存器,或者是栈数据,这样的数据在任务切换的时候会进行压栈保护,任务返回的时候,则会恢复这些寄存器,所以还是不会改变。
楼上兄台,能举个例子说明多条汇编语句组成的一条C语言赋值语句是会被任务切换改变的吗?

使用特权

评论回复
13
ShakaLeo| | 2011-2-24 14:37 | 只看该作者
“虽然是分4条指令完成,但都是调用的寄存器,或者是栈数据”
局部变量是这样的,全局变量不是。每个全局变量是有它固定的地址的,如果对某个全局变量的存取不是一条指令能够完成,就需要临界保护。OSIdleCtr正是全局变量。

使用特权

评论回复
14
john_lee| | 2011-2-24 14:58 | 只看该作者
原则:
1、对于作用域和生存期仅限于当前函数的数据(寄存器和栈上),不需保护。
2、作用域或生存期超出了当前函数的数据(全局、静态、堆),需检查逻辑上的共享冲突(中断访问,多个任务使用,特别注意函数重入),如果存在共享冲突,则需保护。

关于例子,不必举了,简单想一下就明白了:假设存在共享冲突而没有保护,当任务A在写数据时,写了一半,中断发生,导致任务切换,任务B取数据,而这个数据是不完整的,很可能导致随后的处理错误。

使用特权

评论回复
15
sxx7777| | 2011-2-24 15:42 | 只看该作者
新手学习中

使用特权

评论回复
16
电子乌托邦|  楼主 | 2011-2-24 15:55 | 只看该作者
原则:
关于例子,不必举了,简单想一下就明白了:假设存在共享冲突而没有保护,当任务A在写数据时,写了一半,中断发生,导致任务切换,任务B取数据,而这个数据是不完整的,很可能导致随后的处理错误。
john_lee 发表于 2011-2-24 14:58

谢谢john_lee回复,
依旧不理解,不理解数据不完整的含义。
再次感谢。
哪位大侠能给举个例子?

使用特权

评论回复
17
linqing171| | 2011-2-24 21:24 | 只看该作者
2# ayb_ice

假设:
OSIdle为一个3的倍数,如果不是3的倍数,系统就是发生了致命错误.
另外有个用户界面显示当前的OSIdleCtr是多少. 当前OSIdleCtr为0x0102,
上面的OSIdleCtr=0L; 在51里面会有多条语句来赋值.
刚好改了一半的时候,界面线程打断并读取出去这个数,无论0x0100,还是0x0002,都不是正常的数,也不是历史上出现过的数.
数据的完整性就被破坏了.
界面上可以显示0x0102,也可以显示0x0000 这两个都是完整的.

使用特权

评论回复
18
电子乌托邦|  楼主 | 2011-2-25 08:56 | 只看该作者
谢谢楼上所言,有所感悟!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

140

主题

633

帖子

2

粉丝