C语言 在具有中断的循环缓冲区中使用volatile

4zcjmb1e  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(108)

在实现循环缓冲区时,我正在努力正确使用volatile关键字。缓冲区在ISR中写入,在主程序中读取。我运行的是裸机微控制器,所以没有多线程。
我当前的实现看起来像这样:

volatile char circ_buf[n] = {0};
volatile size_t head_index = 0;
volatile size_t tail_index = 0;

void buffer_input(const char c)
{
    if((head_index+ 1) % n != tail_index) {
        //Only if buffer is not full
        circ_buf[head_index] = c;
        head_index = (head_index + 1) % n;
    }
}

char buffer_read(void)
{
    if (tail_index != head_index)
    {
        char c = circ_buf[tail_index];
        tail_index = (tail_index + 1) % n;
        return c;
    }
    //Lets assume 0 will never be content of the buffer for this example
    return 0;
}

buffer_input从ISR调用,buffer_read从主程序调用。
这个实现是可行的,但是我在某个地方读到过,不需要使缓冲区数组本身易失,因为内容只能通过易失索引访问。是真的吗?内容在中断和主程序中都可以访问,所以在我的理解中,缓冲区也应该是易失性的?

xxb16uws

xxb16uws1#

这个实现是可行的,但是我在某个地方读到过,不需要使缓冲区数组本身易失,因为内容只能通过易失索引访问。是真的吗?
否,因为如果circ_buf不符合volatile,则C标准不要求程序在第二次或后续时间使用缓冲区的元素时重新读取该元素,无论索引是否易失。
另一方面,是的,但很危险。围绕volatile的规则的原因是要求C实现重新读取可能更改的内容(类似于写入必须对硬件可见的内容,但当我们只考虑buffer_read例程时,这不是一个问题),并且不执行可以省略重新读取的优化。我们可以推断,由于缓冲区的使用方式以及它包含的元素数量(如果它足够大),编译器无法进行合理的优化来允许它缓存缓冲区元素。这是一种危险的方法,不会在正常的软件工程中使用,但我将其包括在内,以使答案完整。
如果x是volatile的,那么int t0 = x, t1 = x;需要读取x两次。如果x不是volatile的,编译器可以很容易地将其优化为只读取x一次。但是考虑到buffer_read是如何遍历缓冲区的,编译器不可能读取buffer[0]一次,然后在读取整个缓冲区并返回到开始时保留一个副本以供重用。编译器必须重新读取buffer[0],因为它没有实际的方法来避免它。因此,在实践中,如果您从circ_buf中省略volatile,则不会看到程序故障。
然而,volatile是正确的。如果没有它,C标准所规定的C源代码的含义就不是程序所期望的含义,即从内存中读取ISR更新的circ_buf[tail_index]的最新值。

相关问题