位域结构体多线程访问出错的问题分析
位域结构体能节省一些内存空间,但是使用不当会产生race conditions,导致程序异常,下面简要分析错误产生的原因和解决方案。
首先定义一个简单的bit field结构体。
+struct bit_filed { + unsigned a : 1; + unsigned b : 1; + unsigned c : 1; + unsigned d : 1; + unsigned e : 1; + unsigned f : 1; + unsigned g : 1; + unsigned h : 1; +} val; + + +int test_bitfield1(void) +{ + val.a = 1; + return 0; +} + +int test_bitfield2(void) +{ + val.b = 1; + return 0; +}
然后反汇编
ffffffc0005421e8 <test_bitfield1>: ffffffc0005421e8: f0000fa1 adrp x1, ffffffc000739000 <__hyp_idmap_text_end+0x4800> ffffffc0005421ec: f9426821 ldr x1, [x1,#1232] ffffffc0005421f0: 52800000 mov w0, #0x0 // #0 ffffffc0005421f4: 39400022 ldrb w2, [x1] ffffffc0005421f8: 32000042 orr w2, w2, #0x1 ffffffc0005421fc: 39000022 strb w2, [x1] ffffffc000542200: d65f03c0 ret ffffffc000542204 <test_bitfield2>: ffffffc000542204: f0000fa1 adrp x1, ffffffc000739000 <__hyp_idmap_text_end+0x4800> ffffffc000542208: f9426821 ldr x1, [x1,#1232] ffffffc00054220c: 52800000 mov w0, #0x0 // #0 ffffffc000542210: 39400022 ldrb w2, [x1] ffffffc000542214: 321f0042 orr w2, w2, #0x2 ffffffc000542218: 39000022 strb w2, [x1] ffffffc00054221c: d65f03c0 ret
可以看到,这种情况下最小访存单元是一个byte,在上面标黄的指令中会从内存读取一个副本,如果test_bitfield1在执行黄色指令后被test_bitfield2强行插入,test_bitfield2执行完毕后test_bitfield1继续执行,
在这种情况下test_bitfield2所做的修改会被丢弃。
所以平时如果需要用到bit filed struct, 需要注意:
1. 不同位域变量是不是在一个storage unit(可以通过反汇编查看)里面
2. 有没有被并发访问的可能。
下面这篇文章分析了这个问题并给出了几种解决方案:
- One approach for preventing data races in concurrent programming is to use a mutex
- Another approach is to insert a non-bit-field member between any two bit-fields to ensure that each bit-field is the only one accessed within its storage unit.
- Use distinct non-bit-field members of a structure