![Lee_的头像 Lee_的头像](https://cdn.eetrend.com/files/styles/picture200/public/pictures/picture-147-1476977402.jpg?itok=DhnfPdhO)
原子读操作是在MCU并发编程中常用的操作,简单举个例子来阐述问题:
我们使用RTOS或裸机状态编程时,必然需要一个全局时钟基准,通常是在一个定时器中断中累加实现,简化代码如下:
static unsigned long volatile __jiffies = 0; /* 全局时钟基准节拍累加器 */
ISR_TIMER() /* 定时中断服务函数 */
{
++__jiffies;
/* 其它代码...: */
}
对于其中的__jiffies变量,就是全局时间基准,程序中其它地方都会对其进行原子读操作来判断时间,典型的接口实现如下:
unsigned long get_jiffies(void)
{
unsigned long tmp;
CLOCK_IRQ_DIS(); /* 关定时中断 */
tmp = __jiffies;
CLOCK_IRQ_EN(); /* 开定时中断 */
return tmp;
}
请注意,其中关于对中断的开关是对该定时中断中所有代码会带来影响。如果在RTOS中,关中断的时间是一种重要性能指标,决定了整个系统的中断快速响应能力。
在此假设一个最艰难的架构,8位机(AVR、51等等),其上只有8位单字节数据的读写是单指令原子的,其中unsigned long型在这样的架构下是32位8字节。
根据各位朋友提出情况,进行说明:
1、有朋友认为读操作没必要关中断.
这个显然不可能,当你读了32位变量任何一个字节的时候,剩下的7个字节都可能改变。
2、认为在中断函数建立数据拷贝
这个理由同上,无论如何复制,都难以避免读的瞬间数据被破坏
3、建立单字节原子锁
该体系必须支持测试清零指令,而且就算支持。如果中断里发现锁被占有了,那这个周期还能进行+1操作么?无论是用变量缓存还是丢弃,所记时间都不准了。
实现如下:
unsigned long get_jiffies(void)
{
unsigned long tmp;
do {
tmp = __jiffies;
} while(tmp != __jiffies);
return tmp
}
简单得大家可能都不相信,可以满足任何MCU架构完成如上对__jiffies变量的操作(必须单核),大家可以仔细想想。
无锁单读单写队列是MCU上经常用的,对中断通信接口的缓冲非常方便可靠。以此为基础,可跨平台实现。