djyos的笔记 https://passport2.21ic.com/?497911 [收藏] [复制] [RSS]

日志

关中断/禁调度/信号量/原子变量的区别和用法

已有 2033 次阅读2007-11-10 09:04 |系统分类:嵌入式系统

    有人问及关中断和保护全局变量的问题,还有人以为开中断时会导致cpu不停地查看有没有中断而变慢,我答及:

关于中断使cpu变慢的问题:

1、cpu的中断管理和指令执行(运算器)是两套硬件,他们互相独立又有关联。运算器忠实地执行由取指器取得的指令,而中断管理器是通过影响取指地址来使cpu执行中断处理程序的。
2、无论中断是否允许,运算器都按自己的节奏工作,无须花时间去查询是否由中断到达,因此,cpu在任何时候都不会因中断而变慢,中断只是使主程序看起来变慢了,只要没有有效中断发生,cpu执行主程序也不会变慢。
3、中断管理器则不断地探测是否有中断信号到达,若有且中断允许(即发生了有效中断),则保存当前执行状态信息,然后打断当前取指序列,强行转到特定地址(中断向量)取指令,整个过程运算器并不知道,它只是忠实地执行取指电路取得的指令。 
        为保证正确访问临界数据区(共享资源)和正确执行临界代码段,操作系统需要使某些关键操作保持同步,操作系统一般提供“关中断、关调度、信号量”来用于线程(进程)同步,还有些操作系统提供原子变量的方法,linux中广为人知的锁其实是用信号量实现的。那么,这么多的方法中,什么情况适用哪一种方法呢?是有规律的。
1、原子变量
        原子变量可以保证一个变量单次操作的正确性,其保护甚至比信号量还完善,信号量只能保护全局数据不被其他线程破坏,而原子变量能保证全局数据不被中断破坏。
example1: 
int   a;
int   b,c;
a   =   a   +   b   +   c;
上述代码中,cpu对a有一个读、修改、写的过程,这个过程如果被打断,并在其他线程中修改了a值,执行结果将出现错误,而原子变量将保证不会发生这样的错误。 把上述代码改为:

atomic_t a;
int   b,c; 
atmotic_add(&a ,   b   +   c);
原子变量能保证变量一次操作的原子性,并不能保证一系列操作的原子性,若把上述代码改为
example2:
atomic_t   a;
int   b,c;
atmotic_add(&a ,   b ); //L1
atmotic_add(&a ,   c );  //L2
原子变量不能保证L1和L2两行程序间a不被其他线程修改,因此example2不一定能得到正确的结果。
2、信号量 
        原子变量并非所有操作系统都会提供,但几乎所有操作系统都提供信号量机制,用于保护临界代码。信号量能设定并发访问次数限制n,够保证被信号量保护的代码不会发生超过n次的并发访问,如果n=1,就是所谓的二值信号量,二值信号量禁止对被保护资源的任何并发访问。上述example2不能用原子变量保护,但可以用信号量保护,改成:
获取信号量;
a   =   a   +   b; //L1
a   =   a   +   c; //L2
释放信号量;
当一个全局变量可能被多个线程操作时,或者有多个线程或进程需要使用独占资源时,就应该用信号量保护,注意,不能在中断处理程序中访问用信号量保护的变量。
3、关调度
        关调度是一种比较粗暴的方式,关调度后,操作系统不会再进行线程上下文切换,而是专心执行一个线程,但是中断仍然开着。关调度可以起到替代信号量保护全局变量的作用,但一般不这样用,太粗暴了。但是如果某一段代码的执行时间有要求,希望cpu全速执行不被打断,但又不希望关中断时,可用关调度的方法。注意,从逻辑上,关调度能替换信号量,但不能替换原子变量。
4、关中断
        关中断是最粗暴的一种方式,用于保证最严格的时序执行,比如某段代码要在IO上输出两个高精度脉冲,脉冲宽度2uS,间隔2uS,这种需求只能通过用精确的指令延时来实现,延时过程中,如果被中断,或者发生线程切换,将不能正确输出脉冲。从逻辑上,前面所讲的三种保护,都可以用关中断实现,只是,太粗暴了,软件不相信暴力!

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)