区分单独单写、单写多度、多写多
区分操作数长度(很多体系结构、操作系统、甚至编译器、对短数据类型都有原子操作的)
最后才考虑关中断、互斥锁之类的常规保护
关键字 volatile 的使用,原子操作,临界区的使用。 全局变量的使用及保护处查看。
1.关键字 volatile
关键字 volatile 用于告诉编译器,说明被修身的变量可能会被意想不到地改变,防止编译器对代码进行优化。
比如如下程序:
1 ucNms=0x65;
2 ucNms=0x66;
3 ucNms=0x67;
4 ucNms=0x68;
上述 4 条语句,如果变量在声明的时候(unsigned char ucNms;)没有使用 volatile,那么编译器有可能对其优化,只编译最后一条语句 ucNms=0x68;(即忽略前三条语句,只产生一条机器汇编代码);如果变量在声明的时候(volatile unsigned char ucNms;)使用了 volatile,则编译器会逐一地进行编译并产生四条相应的机器代码(产生四条代码)。
精确地说就是,编译器在编译这个变量语句时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。所以下面几个情况在声明的时候需要用 volatile 关键字对其修饰:
1)并行设备的硬件寄存器(如:状态寄存器)
2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3)多线程应用中被几个任务共享的变量
2.原子操作
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)
原子操作可以理解为不被打断的操作,可以是一个步骤的操作,也可以是多个步骤的操作,总之确保操作不被打断。
3.临界区
指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。
中断全局变量尽量要用 volatile 修饰,中断全局变量要原子操作访问,要时刻明白中断全局变量是临界区资源,共享访问时需要保护。
另外还有一种无原子锁读操作的方法,由于性能明显低于开关中断的方法,并不推荐对于中断比较敏感的系统也可采用。下面贴出参考代码如下:
4.全局变量的使用及保护
单片机裸机编程,使用全局变量时,一般是一个或多个*.c 文件(或模块)中会使用到某个全局变量(假设为 A),还有中断中也会用到这个全局变量。这样在使用时就要考虑变量的安全性。
首先要明白大循环(后台)对这个变量的访问是依次的,不管全局变量 A 是在哪一个模块或者*.c 文件中,每一个时刻只有一个地方对变量 A 访问。然后中断和中断嵌套程序中也会有对全局变量 A 的访问。
于是就存在这样的问题,大循环(后台)在访问全局变量 A 时(比如说访问到一半时),被中断(前台)程序打断并修改了全局变量 A,这样大循环(后台)程序再次对全局变量 A 访问,就会导致访问到的 A 存在不确定性。从而会影响程序的不正常运行。
这样就可以很明确的知道,只要在大循环(后台)访问 A 时,不让中断(前台)打断其访问即可。确保对 A 的访问是原子操作。于是就有这样的解决方法:
关中断-->>全局变量 A-->>开中断 有的时候,如果访问变量 A 的过程比较长,可以对全局变量 A 做一个副本拷贝 a,用拷贝的 a 作为模块处理的数据。于是就有了这样:
关中断-->>访问全局变量 A-->>副本拷贝 a-->>开中断->>操作副本拷贝 a 这种复杂的情况也可以做一个锁这样做:
大循环(后台):
关中断-->>上锁-->>开中断-->>访问变量 A-->>关中断-->>解锁-->>开中断 中断(前台):
如果是解锁的,操作全局变量 A,如果是上锁的就不操作 当然,如果访问全局变量 A 本身就是一个原子操作(比如一条指令就可以访问完成),这样也就不需要做开关中断的处理了。
示例 1:禁止中断方法保护全局变量
大循环(后台)
ET0=0; //禁止定时中断
访问全局变量 A;
其他代码部分;
ET0=1; //开启允许定时中断
定时器中断(前台)
操作全局变量 A;
示例 2:加锁的方法保护全局变量
大循环(后台)
ET0=0; //禁止定时中断
Lock = 1;
ET0=1; //开启允许定时中断
访问全局变量 A;
其他代码部分;
ET0=0; //禁止定时中断
Lock =0;
ET0=1; //开启允许定时中断
定时器中断(前台)
If(lock ==0) 操作全局变量 A;
else{;} 示例 3:加锁的方法保护全局变量
大循环(后台)
Lock = 1;//若此条语句对应汇编指令是原子操作可以不用开关中断保护此锁
访问全局变量 A;
其他代码部分;
Lock =0;//若此条语句对应汇编指令是原子操作可以不用开关中断保护此锁
定时器中断(前台)
If(lock ==0) 操作全局变量 A;
else{;}
总结下:中断全局变量尽量要用 volatile 修饰,中断全局变量要原子操作访问,要时刻明白中断全局变量是临界区资源,共享访问时需要保护。
另外还有一种无原子锁读操作的方法,由于性能明显低于开关中断的方法,并不推荐对于中断比较敏感的系统也可采用。下面贴出参考代码如下:
AType getA(void){ AType tmp; do { tmp = A; } while(tmp != A); return tmp } 就是两次读取中断中的全局变量 A,判断两次读取值是否相等,相等的话就认为线程中对全局变量 A 的访问没有被打断,返回的访问值是安全可靠的。