关于环形缓冲区(ringbuffer)的使用安全
一.preface
看过许多文章,关于ringbuffer在多线程环境下的使用,或者在中断写、thread读的情况下的使用,一般都是需要加锁,来保证同步。
但是在我开发项目的过程中,发现ringbuffer的读写并不需要加锁,资源也不会有竞争。
二.analysis
环形缓冲区是在一端写,另一端读;写完更新write_index, 读完更新read_index.
以读操作为例,进行分析。
点击查看代码
/**************C************/
int8_t ringbuffer_read(uint8_t *ch, ringbuffer_t *rb)
{
if (rb->read_index == rb->write_index) // 读写指针的位置相等时,说明 ringbuffer 为空。
{
return ERR_BUF_EMPTY;
}
else
{
*ch = rb->buffer_ptr[rb->read_index];
rb->read_index = (rb->read_index + 1) % rb->buffer_size;
return ERR_OK;
}
}
/**************汇编************/
ringbuffer_read
0x010020b8: b510 .. PUSH {r4,lr} //函数入口,保存寄存器 r4 和 lr 到栈中,以保护函数调用时的上下文。
/* uint8_t *ch, ringbuffer_t *rb */
0x010020ba: 460c .F MOV r4,r1 //将 r1(第二个参数 rb)的值移动到 r4 中,方便后续操作。
/* if (rb->read_index == rb->write_index) */
0x010020bc: 6809 .h LDR r1,[r1,#0] // 加载 rb->read_index 到 r1
0x010020be: 6862 bh LDR r2,[r4,#4] // 加载 rb->write_index 到 r2
0x010020c0: 4291 .B CMP r1,r2 // 比较 r1 和 r2
0x010020c2: d101 .. BNE 0x10020c8 ; ringbuffer_read + 16 // 如果不相等,跳转到缓冲区不为空的处理
/* return ERR_BUF_EMPTY; */
0x010020c4: 2008 . MOVS r0,#8 // 将返回值 ERR_BUF_EMPTY (8) 放入 r0
0x010020c6: bd10 .. POP {r4,pc} // 恢复寄存器并返回
/* *ch = rb->buffer_ptr[rb->read_index]; */
0x010020c8: 6822 h LDR r2,[r4,#0] // 重新加载 rb->read_index 到 r2
0x010020ca: 68e1 .h LDR r1,[r4,#0xc] // 加载 rb->buffer_ptr 到 r1
0x010020cc: 5c89 .\ LDRB r1,[r1,r2] //读取一个字节到 r1
0x010020ce: 7001 .p STRB r1,[r0,#0] // 将 r1 的值存储到 *ch
/* rb->read_index = (rb->read_index + 1) % rb->buffer_size; */
0x010020d0: 68a1 .h LDR r1,[r4,#8]
0x010020d2: 6820 h LDR r0,[r4,#0]
0x010020d4: 1c40 @. ADDS r0,r0,#1
0x010020d6: f7feffe3 .... BL __aeabi_uidiv ; 0x10010a0
0x010020da: 6021 !` STR r1,[r4,#0]
/* return ERR_OK; */
0x010020dc: 2000 . MOVS r0,#0
0x010020de: bd10 .. POP {r4,pc}
浙公网安备 33010602011771号