×
嵌入式 > 技术百科 > 详情

MCU上的无锁原子读操作

发布时间:2020-06-02 发布时间:
|
原子读操作是在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上经常用的,对中断通信接口的缓冲非常方便可靠。以此为基础,可跨平台实现。
 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
实时控制.安全.如何加速实现未来工厂落地?