原子/易失性/同步如何在内部工作?
以下代码块之间有什么区别?
代码1
private int counter;
public int getNextUniqueIndex() {
return counter++;
}
Run Code Online (Sandbox Code Playgroud)
代码2
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
Run Code Online (Sandbox Code Playgroud)
代码3
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
Run Code Online (Sandbox Code Playgroud)
是否volatile以下列方式工作?是
volatile int i = 0;
void incIBy5() {
i += 5;
}
Run Code Online (Sandbox Code Playgroud)
相当于
Integer i = 5;
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
Run Code Online (Sandbox Code Playgroud)
我认为两个线程不能同时进入同步块...我是对的吗?如果这是真的那么如何atomic.incrementAndGet()工作没有synchronized …
我一直在为ARM的嵌入式操作系统工作,但是即使在引用ARMARM和linux源代码之后,我仍然对架构有一些了解.
原子操作.
ARM ARM表示加载和存储指令是原子的,并且在执行中断处理程序之前保证其执行完整.通过查看验证
arch/arm/include/asm/atomic.h :
#define atomic_read(v) (*(volatile int *)&(v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))
Run Code Online (Sandbox Code Playgroud)
但是,当我想使用对于ARMv7(我的目标)使用LDREX和STREX的cpu指令(atomic_inc,atomic_dec,atomic_cmpxchg等...)原子地操作此值时,问题就出现了.
ARMARM没有说明在本节中阻止中断的任何内容,因此我假设在LDREX和STREX之间可能发生中断.它提到的事情是关于锁定内存总线,我猜这只对MP系统有帮助,因为MP系统可能有更多的CPU试图同时访问同一个位置.但对于UP(可能还有MP),如果在LDREX和STREX的这个小窗口中触发定时器中断(或SMP的IPI),异常处理程序执行可能会更改cpu上下文并返回到新任务,但是令人震惊的部分现在进入,它执行'CLREX',因此删除前一个线程持有的任何独占锁.那么在UP系统上使用LDREX和STREX比LDR和STR在原子性方面有多好?
我确实读过一些关于独占锁监视器的东西,所以我有一个可能的理论,当线程恢复并执行STREX时,os监视器会导致此调用失败,可以检测到并且可以使用new重新执行循环过程中的价值(分支回LDREX),我在这里吗?
在ARM架构上,遗憾的是我不确切知道它是什么芯片,是32位int读/写原子?
对基本类型的读/写有什么保证吗?
注意:对于这个问题,我不是在谈论 C 或 C++语言标准。相反,我谈论的是针对特定体系结构的 gcc 编译器实现,因为语言标准对原子性的唯一保证是使用_AtomicC11 或更高版本中的类型或std::atomic<>C++11 或更高版本中的类型。另请参阅我在这个问题底部的更新。
在任何体系结构上,某些数据类型可以原子方式读取和写入,而其他数据类型则需要多个时钟周期,并且可能在操作中间被中断,如果跨线程共享该数据,则会导致损坏。
在8 位单核 AVR 微控制器(例如:Arduino Uno、Nano 或 Mini 使用的 ATmega328 mcu)上,只有8 位数据类型具有原子读取和写入(使用 gcc 编译器和gnu C 或gnu C++语言)。我在不到 2 天的时间里进行了 25 小时的马拉松式调试,然后在这里写下了这个答案。另请参阅此问题的底部以获取更多信息。以及有关使用 AVR-libc 库的 gcc 编译器编译时 AVR 8 位微控制器具有自然原子写入和自然原子读取的 8 位变量的文档。
在(32 位)STM32 单核微控制器上,任何32 位或更小的数据类型绝对自动是原子的(当使用 gcc 编译器和 gnu C 或 gnu C++ 语言编译时,因为ISO C 和 C++ 不保证这一点直到 2011 年版本,_Atomic类型为 C11 …
通过“原子访问防护”或“中断防护”强制对与 ISR 共享的易失性变量进行原子访问的标准技术,特别是在没有操作系统的情况下运行裸机、单线程协作多任务应用程序时,如下所示:
// 1. save interrupt state
// 2. disable only the interrupts necessary
// You get atomic access to volatile variables shared with ISRs here,
// since ISRs are the only other "context" or running "thread" which
// might attempt to modify a shared memory block or variable.
// 3. restore interrupt state
Run Code Online (Sandbox Code Playgroud)
另请参阅我在这里详细描述的地方,包括最佳实践(在短时间内保持中断关闭)以及如何通过我的doAtomicRead()重复读取循环函数进行原子读取而不首先禁用中断:读取 64 位变量,即由 ISR 更新。
我之前已经记录过如何对 AVR 微控制器/Arduino 执行此操作:How do I Forceatomity in Atmel AVR mcus/Arduino? …
我没有找到太多关于非原子操作的材料。
\n假设我有一个 32 位处理器,并且我想在 64 位变量中保留微秒计数。中断将每微秒更新一次变量。调度程序是非抢占式的。将有一个函数用于清除变量,另一个函数用于读取变量。由于它是 32 位处理器,因此访问将是非原子的。是否有 \xe2\x80\x9cstandard\xe2\x80\x9d 或惯用的处理方法,以便读取器函数不会获得半更新的值?
\n我目前正在费力地阅读 ARMv7 内核的 ARM 架构手册。在关于内存访问原子性的章节 A3.5.3 中,它指出:
如果单副本原子加载与单副本原子存储重叠,并且对于任何重叠字节,加载返回由单副本原子存储插入到该字节的一致性顺序中的写入写入的数据,则加载必须返回来自一致性顺序中的某个点的数据不早于由所有重叠字节的单副本原子存储插入一致性顺序的写入。
作为非英语母语人士,我承认我在理解这句话时遇到了一些挑战。
是否存在对内存字节的写入未插入一致性顺序因此上述情况不适用的情况?如果不是,我的说法是否正确,将句子缩短并改写为以下内容:
如果加载恰好返回写入的至少一个字节,则加载必须从不早于写入将它们插入所有重叠字节的一致性顺序的点返回所有重叠字节。
仍然传达相同的含义吗?
我只是失去了几天,从字面上看,大约25个小时的工作,因为我试图调试我的代码而不是我不知道的简单.
事实证明,在C++中减少单字节数组的元素,在AVR上,ATmega328 8位微控制器(Arduino)不是原子操作,需要原子访问保护(即关闭中断).为什么是这样???此外,在Atmel AVR微控制器上确保原子访问变量的所有C技术是什么?
这是我所做的一个愚蠢的版本:
//global vars:
const uint8_t NUM_INPUT_PORTS = 3;
volatile uint8_t numElementsInBuf[NUM_INPUT_PORTS];
ISR(PCINT0_vect) //external pin change interrupt service routine on input port 0
{
//do stuff here
for (uint8_t i=0; i<NUM_INPUT_PORTS; i++)
numElementsInBuf[i]++;
}
loop()
{
for (uint8_t i=0; i<NUM_INPUT_PORTS; i++)
{
//do stuff here
numElementsInBuf[i]--; //<--THIS CAUSES ERRORS!!!!! THE COUNTER GETS CORRUPTED.
}
}
Run Code Online (Sandbox Code Playgroud)
这是循环的版本,没关系:
loop()
{
for (uint8_t i=0; i<NUM_INPUT_PORTS; i++)
{
//do stuff here
noInterrupts(); //globally disable interrupts
numElementsInBuf[i]--; //now it's ok...30 hrs of …Run Code Online (Sandbox Code Playgroud) 我像这样编写STM8 GPIO,PD_ODR_ODR4 = 1;但stm32f10x.h没有此功能,是否有任何.h文件定义了位。
抱歉,我不知道如何更好地解释这个问题。
我尝试了多个GPIO库。
强文本