C 哪些变量类型/大小是STM32微控制器上的原子变量?
以下是STM32微控制器上的数据类型: 这些微控制器使用32位ARM核心处理器 哪些数据类型具有自动原子读取和原子写入访问权限 我很确定所有32位数据类型都可以(因为处理器是32位的),而所有64位数据类型都不能(因为读或写一个64位字至少需要2个处理器操作),但是C 哪些变量类型/大小是STM32微控制器上的原子变量?,c,arm,atomic,stm32,freertos,C,Arm,Atomic,Stm32,Freertos,以下是STM32微控制器上的数据类型: 这些微控制器使用32位ARM核心处理器 哪些数据类型具有自动原子读取和原子写入访问权限 我很确定所有32位数据类型都可以(因为处理器是32位的),而所有64位数据类型都不能(因为读或写一个64位字至少需要2个处理器操作),但是bool(1字节)和uint16\u t/int16\u t(2字节)呢 上下文:我在STM32上的多个线程之间共享变量(单核,但多线程,或者在中称为“任务”),我需要知道是否需要通过关闭中断、使用互斥体等来强制原子访问 更新: 参考
bool
(1字节)和uint16\u t
/int16\u t
(2字节)呢
上下文:我在STM32上的多个线程之间共享变量(单核,但多线程,或者在中称为“任务”),我需要知道是否需要通过关闭中断、使用互斥体等来强制原子访问
更新:
参考此示例代码:
volatile bool shared_bool;
volatile uint8_t shared u8;
volatile uint16_t shared_u16;
volatile uint32_t shared_u32;
volatile uint64_t shared_u64;
volatile float shared_f; // 32-bits
volatile double shared_d; // 64-bits
// Task (thread) 1
while (1)
{
// Write to the values in this thread.
// What I write to each variable will vary. Since other threads
// are reading these values, I need to ensure my *writes* are atomic, or else
// I must use a mutex to prevent another thread from reading a variable in the middle
// of this thread's writing.
shared_bool = true;
shared_u8 = 129;
shared_u16 = 10108;
shared_u32 = 130890;
shared_f = 1083.108;
shared_d = 382.10830;
}
// Task (thread) 2
while (1)
{
// Read from the values in this thread.
// What thread 1 writes into these values can change at any time, so I need to ensure
// my *reads* are atomic, or else I'll need to use a mutex to prevent the other
// thread from writing to a variable in the midst of reading
// it in this thread.
if (shared_bool == whatever)
{
// do something
}
if (shared_u8 == whatever)
{
// do something
}
if (shared_u16 == whatever)
{
// do something
}
if (shared_u32 == whatever)
{
// do something
}
if (shared_u64 == whatever)
{
// do something
}
if (shared_f == whatever)
{
// do something
}
if (shared_d == whatever)
{
// do something
}
}
在上面的代码中,我可以在不使用互斥锁的情况下为哪些变量执行此操作?我的怀疑如下:
volatile bool
:安全--无需互斥volatile uint8\u t
:安全--无需互斥volatile uint16\u t
:安全--无需互斥volatile uint32\u t
:安全--无需互斥volatile uint64\u t
:不安全--必须使用关键节或互斥锁李>
volatile float
:安全--无需互斥volatile double
:不安全--必须使用关键部分或互斥锁李>
FreeRTOS的关键部分示例:- 相关,但不回答我的问题:
取决于你所说的原子 如果不是简单的加载或存储操作,如
a += 1;
那么,并非所有类型都是原子的
如果是简单的存储或加载运算,32位、16位和8位数据类型是原子的。如果寄存器中的值必须标准化,则8和16位存储和加载可能不是原子的
如果硬件支持位带,那么如果使用位带,则支持位带的内存区域中的位操作(设置和重置)是原子的
注意。
如果您的代码不允许未对齐操作,则8位和16位操作可能不是原子操作。有关此问题的最终确定答案,请直接跳到下面标题为“我的问题的最终答案”的部分 2018年10月30日更新:我无意中引用了(稍微)错误的文档(但上面说的是完全相同的事情),所以我在这里的回答中修复了它们。有关详细信息,请参阅本答案底部的“2018年10月30日变更注释” 我肯定不理解这里的每一个字,但是ARM v7-M架构参考手册(;)(不是技术参考手册[TRM],因为它没有讨论原子性)验证了我的假设: 所以…我认为我问题底部的7个假设都是正确的。[2018年10月30日:是的,这是正确的。详情见下文。]
2018年10月29日更新: 还有一个小道消息: FreeRTOS创始人、专家和核心开发人员Richard Barry在
tasks.c
中指出
/*由于变量的类型为BaseType\u t,因此不需要临界段*/
…在STM32上读取“无符号长”(4字节)易失性变量时这意味着,他至少100%确定4字节读写在STM32上是原子的。他没有提到较小的字节读,但对于4字节读,他完全肯定。我必须假设4字节变量是本机处理器的宽度,而且,这对于实现这一点至关重要
例如,在FreeRTOS v9.0.0的tasks.c
中,第2173-2178行:
UBaseType_t uxTaskGetNumberOfTasks( void )
{
/* A critical section is not required because the variables are of type
BaseType_t. */
return uxCurrentNumberOfTasks;
}
他用的正是这个短语
/*由于变量的类型为BaseType\u t,因此不需要临界段*/
…位于此文件中的两个不同位置
我问题的最终答案:所有类型的原子“算术”都可以由CPU核心寄存器处理
它可以是任何类型,一个或四个字节取决于体系结构和指令集
但是修改内存中的任何变量至少需要3个系统步骤:RMW=读取内存以注册、修改寄存器和将寄存器写入内存。
所以,只有当您控制CPU寄存器的使用时,原子修改才有可能,这意味着需要使用纯汇编程序,而不使用C或Cpp编译器
当您使用C\Cpp编译器时,它会将全局或全局静态变量放在内存中,因此C\Cpp不会提供任何原子操作和类型
注:例如,您可以使用“FPU寄存器”进行原子修改(如果您确实需要),但您必须对编译器和RTO隐藏该体系结构具有FPU。这将是特定芯片的ARM指令集手册的目的?可能的副本您必须查看汇编代码。您是否试图防御在相同数据上运行的两个内核,或者在一个写的中间被打断以在同一个内核上的另一个线程?后者:“在一个写的中间被打断,以达到同一个内核上的另一个线程”。谢谢你的回答。请查看mu更新的问题,并查看是否可以更明确地验证我的怀疑。顺便说一句,
a+=1
是两个操作,不是原子操作。同意。几年前,我在一个8位AVR处理器上通过增加(不是原子操作)一个本来具有原子读写能力的8位变量,艰难地学会了这一点。@zneak no,只有当操作是RMW时,否则它是原子的。它可能不是一致的(缓存),而是原子的。如果该操作是原子的,则多个内核同时尝试该操作将成功。事实并非如此,如果在一个循环中有两个内核这样做,那么肯定会损失一些增量
UBaseType_t uxTaskGetNumberOfTasks( void )
{
/* A critical section is not required because the variables are of type
BaseType_t. */
return uxCurrentNumberOfTasks;
}