Linux kernel提供了一些原子位操作的API,比如通过set_bit(int nr, void *addr)设置addr上的第nr位,而clear_bit(int nr, void *addr)用来清除addr上的第nr位。
关于使用原子位操作和非原子位操作的区别,《Linux内核设计与实现》一书中,描述的不是很清楚,很容易引起歧义:
“For example, assume you issue two atomic bit operations: Initially set the bit and then clear the bit. Without atomic operations, the bit might end up cleared, but it might never have been set.”
其实可以这样理解,与对变量进行加加、减减等操作类似,置位操作对应到C语言虽然只有一条语句,但实际上是由多条汇编指令完成的:
- 读取变量的值到寄存器中
- 对寄存器中的值进行置位操作
- 将寄存器中的值写回变量
考虑这样一种场景,全局变量i的初始值为0,CPU0上的执行线程对其第0位置位,同时CPU1上的执行线程正对其第1位置位:
CPU0 | CPU1 |
---|---|
LOAD i (0) | – |
SETBIT i,0x1 (0x1) | LOAD i (0) |
STORE i (0x1) | SETBIT i,0x2 (0x2) |
– | STORE i (0x2) |
所以当多个执行线程同一时刻对同一变量进行位操作时,就会发生竞争条件。回到上述例子,本来预期的结果是i=0x3, 但当使用非原子位操作时,就有可能得到错误的结果,即i的值是0x1或者0x2。而原子位操作就可以确保结果的正确性,即i=0x3。