我希望能够使用这样的东西来更清楚地访问我的端口:
typedef struct {
unsigned rfid_en: 1;
unsigned lcd_en: 1;
unsigned lcd_rs: 1;
unsigned lcd_color: 3;
unsigned unused: 2;
} portc_t;
extern volatile portc_t *portc;
但安全吗??它对我有用,但是......
1)是否存在竞争条件?
2)gcc是否为修改单个字段的代码生成读 - 修改 - 写周期?
3)是否有更新多个字段的安全方法?
4)包装和订单是否有保证? (在这种情况下我不关心可移植性,所以gcc特定的选项使它做我的意思是好的。)
答案 0 :(得分:1)
处理竞争条件必须通过操作系统级调用(确实使用读 - 修改 - 写)来完成,GCC不会这样做。
同上,并且没有GCC不为volatile
生成读 - 修改 - 写指令。但是,CPU通常会自动执行写操作(因为它只是一条指令)。如果位字段保持在int
范围内,这也适用,但这取决于CPU /实现;我的意思是有些可能保证这个高达8字节的值,而其他只有4字节的值。所以在这种情况下,比特不能混淆(即少数从一个线程写入,而另一个线程中的其他线程不会发生)。
同时设置多个字段的唯一方法是在中间变量中设置这些值,然后将此变量分配给volatile
。
C标准指定比特被打包在一起(当你开始混合类型时似乎有例外,但我从未见过;每个人总是使用unsigned ...
)。
注意:定义某些内容volatile
不会导致编译器生成读取 - 修改 - 写入。 volatile
的作用是告诉编译器必须始终对该指针/地址进行赋值,并且可能不会对其进行优化。
Here's another post关于同一主题。我发现还有很多其他地方可以找到更多细节。
答案 1 :(得分:-1)
关键字volatile与竞争条件或访问代码的线程无关。该关键字告诉编译器不要将值缓存在寄存器中。它告诉编译器生成代码,以便每次访问都转到分配给变量的位置,因为每次访问都可能看到不同的值。存储器映射外设就是这种情况。如果您的MPU拥有自己的缓存,这没有任何帮助。内存映射通常有特殊指令或未缓存区域,以确保读取位置,而不是缓存副本。 至于线程安全,请记住即使是内存访问可能不是线程安全的,它是在两个指令中完成的。例如。在8051汇编器中,您必须一次获得一个16位的值。指令序列可能被IRQ或其他线程中断,第二个字节读取或写入,可能已损坏。