结构体位域并发问题

本文探讨了在并发环境中,结构体位域成员存在的安全问题。由于编译器可能将位域成员压缩存储在同一字节,导致并发修改时的数据不一致。通过分析并发修改位域的示例,揭示了潜在的问题,并提出了两种解决方案:一是加锁保护,二是利用无名位域避免位域成员存储在同一字节。
摘要由CSDN通过智能技术生成

结构体位域并发问题

0x00 位域并发分析

在这里插入图片描述
上图是结构体BitField变量bf所画,需要根据打印推断出各个位域成员的存储情况,具体代码及打印见0x01 代码0x02 打印结果部分。

如上图所示,GCC编译器会对位域成员进行压缩存储(经测试,VS编译器一样会压缩),比如下方示例代码中的位域成员a、b,他们存储在一个字节上。注意位域成员c、d、e及f的低两位,他们存储在一个字节。

计算机是按字节进行操作,即一个字节是基本读写单位。即使是修改一个未占满一个字节的位域成员,计算机同样是覆盖写这个成员所在的整个字节。因上述原因,当两个或多个线程并发的修改同一个字节上的不同位域成员时,在没有并发保护措施下,存在并发安全问题!

下面是验证没有并发保护情况下,一个线程修改成员d,三个线程修改成员f。从打印结果看,对d的第三次取反打印为0,应该打印是1。分析如下:

  1. 修改d和修改f的线程在彼此都没有执行修改前,两个线程读取第二个字节里存储的都是0110 0101
  2. 线程调度,切换到修改d的线程,对d取反赋值给d,再覆盖写整个第二个字节,结果: 0111 0101,此时还未打印,线程调度,切换到修改f的线程。
  3. 修改f,覆盖写整个第二个字节,这里分析就假设f的低两位写为10,此时结果为:1010 0101
  4. 线程调度,切换到修改d的线程,开始打印d的值,发现打印出的d依旧是0

综上,在使用位域时,需要注意这个隐藏的并发问题!

解决上述问题有两种办法,一是对在同一个字节上的位域成员并发修改时,加锁保护。二是使用无名位域,使位域成员不存储在同一字节上即可。

参考资料:POS49-C

0x01 代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

typedef unsigned int uint32;
typedef unsigned char uint8;

typedef struct BitField
{
   
    uint32 a : 4;
    uint32 b : 4;
    uint32 c : 4;
    uint32 d : 1;
    uint32 e : 1;
    uint32 f : 4;
}BitField;

#define LOOP_CNT 10

// 对位于成员d取反
void negate(BitField *bf)
{
   
    for(int i = 0; i < LOOP_CNT; i++)
    {
   
        bf->d = ~bf->d;
      
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值