kk Blog —— 通用基础

date [-d @int|str] [+%s|"+%F %T"]

中断机制

1、

cpu的中断管理和指令执行(运算器)是两套硬件,他们互相独立又有关联。

2、

无论中断是否允许,运算器都按自己的节奏工作,无须花时间去查询是否由中断到达。

3、

中断管理器则不断地探测是否有中断信号到达,若有且中断允许,则保存当前执行状态信息,然后打断当前取指序列,强行转到特定地址(中断向量)取指令,整个过程运算器并不知道,它只是忠实地执行取指电路取得的指令。
因此,只要没有中断信号到达,就不存在cpu边走边看的问题。 为保证正确访问临界数据区和正确执行临界代码段,操作系统一般有:关中断、关调度、信号量,还有些操作系统提供原子变量的方法,linux中广为人知的锁其实是用信号量实现的。那么,这么多的方法中,什么情况适用哪一种方法呢?是有规律的。

1、原子变量

原子变量可以保证一个变量单次操作的正确性,其保护甚至比信号量还完善,信号量只能保护全局数据不被其他线程破坏,而原子变量能保证全局数据不被中断破坏。

1
2
3
4
example1:
	atomic int a;
int b,c;
a = a + b + c;

上述代码中,cpu对a有一个读、修改、写的过程,这个过程如果被打断,并在其他线程中修改了a值,执行结果将出现错误,而原子变量将保证不会发生这样的错误。 原子变量不能保护一系列操作的原子性,若把上述代码改为

1
2
3
4
5
example2:
atomic int a;
int b,c;
a = a + b; //L1
a = a + c; //L2

原子变量不能保证L1和L2两行程序间a不被其他线程修改,因此example2不一定能得到正确的结果。

2、信号量

稍微完善一点的操作系统都提供信号量机制,用于保护临界代码。上述example2就应该用下列代码替代L1和L2:

1
2
3
4
获取信号量;
a = a + b; //L1
a = a + c; //L2
释放信号量;

当一个全局变量可能被多个线程操作时,就应该用信号量保护,注意,不能在中断中访问用信号量保护的变量。

3、关调度

关调度是一种比较粗暴的方式,关调度后,操作系统不会再进行线程上下文切换,而是专心执行一个线程,但是中断仍然开着。关调度可以起到替代信号量保护全局变量的作用,但一般不这样用,太粗暴了。但是如果某一段代码的执行时间有要求,希望cpu全速执行不被打断,但又不希望关中断时,可用关调度的方法。注意,从逻辑上,关调度能替换信号量,但不能替换原子变量。

4、关中断

关中断是最粗暴的一种方式,用于保证最严格的时序执行,比如某段代码要在IO上输出两个高精度脉冲,脉冲宽度2uS,间隔2uS,这种需求只能通过用精确的指令延时来实现,延时过程中,如果被中断,或者发生线程切换,将不能正确输出脉冲。从逻辑上,前面所讲的三种保护,都可以用关中断实现,只是,太粗暴了。