我正在Xv6的操作系统课程中完成一项任务。我需要为进程的创建时间,终止时间,休眠时间等实现数据状态结构......
截至目前,我决定直接使用ticks
变量而不使用tickslock
,因为使用锁定并使系统减速以实现这样的低优先级目标似乎不是一个好主意。
因为ticks变量只是这样使用:ticks++
,有没有办法我会尝试检索当前的刻度数并获得错误的数字?
我不介意错误的数字+10蜱,但有没有办法真正关闭。就像数字01111111111111111将增加时一样,需要更改2个字节。所以我的问题是,存储数据的CPU和另一个CPU是否有可能在存储操作的开始和完成之间获取该存储单元中的数据?
所以在我看来,如果编译器会创建一个mov
指令或inc
指令,我想知道的是它是否可以在它的开始和结束之间看到存储操作。
在asm中没有问题:在x86上使用单个指令完成的对齐加载/存储是原子级到qword(8字节)宽度。 Why is integer assignment on a naturally aligned variable atomic on x86?
(在486上,保证仅适用于4字节对齐值,甚至可能不适用于386,所以可能这就是为什么Xv6使用锁定?我不确定它是否应该是386上的多核安全;我的理解是罕见的386 SMP机器没有完全实现现代x86内存模型(内存排序等)。)
但是C不是asm。一次使用来自多个“线程”的普通非atomic
变量是未定义的行为,除非所有线程都只是读取。这意味着编译器可以假设正常的C变量不会被其他线程异步更改。
在C中的循环中使用ticks
将让编译器read it once and keep using the same value repeatedly。您需要像Linux内核使用的READ_ONCE
宏,例如*(volatile int*)&ticks
。或者简单地将其声明为volatile unsigned ticks;
对于一个足够窄以适合一个整数寄存器的变量,可以安全地假设一个理智的编译器将使用单个双字存储写入它,无论是mov
还是内存目标inc
或add dword [mem], 1
。 (但是,您不能假设编译器将使用内存目标inc / add,因此您不能依赖于中断的单核原子增量。)
有一位作家和多位读者,是的,读者可以简单地阅读它而不需要任何锁定,只要他们使用volatile
。
即使在便携式ISO C中,volatile sig_atomic_t
也有一些非常有限的保证,当由信号处理程序写入并由运行信号处理程序的线程读取时,它可以安全地工作。 (不一定是其他线程,但是:在ISO C中,volatile
不会避免数据竞争UB。但是在x86上使用非恶意编译器实践它很好。)
(POSIX信号是中断的用户空间。)
另见Can num++ be atomic for 'int num'?
对于一个线程来说,在两半中发布更宽的计数器,通常使用SeqLock。有1个写入器和多个读取器,没有实际的锁定,只要读取与读取重叠,读者就可以重试。见Implementing 64 bit atomic counter with 32 bit atomics
首先,使用或不使用锁定不是你的目标是否是低优先级的问题,而是解决race condition的问题。
其次,在您描述的特定情况下,读取ticks变量是安全的,没有任何锁定,因为这不是竞争条件情况,因为RAM访问同一区域(这里甚至相同的地址)不能由2个独立的CPU同时进行( read more)并且因为刻度只是将值增加1并且没有做任何你真正想念的重大变化。