(x | y) - y 为什么不能简单地是 x 甚至是 `x | 0`

RaG*_*__M 47 c bit-manipulation

我正在阅读内核代码,并且在一个地方我看到了if语句中的一个表达式,例如

if (value == (SPINLOCK_SHARED | 1) - 1) {
         ............
}
Run Code Online (Sandbox Code Playgroud)

其中SPINLOCK_SHARED = 0x80000000是预定义的常量。

我想知道为什么我们需要(SPINLOCK_SHARED | 1) - 1- 用于类型转换目的?表达式的结果将是 80000000-- 与 0x80000000 相同,不是吗?然而,为什么 ORing 1 和 Subtracting 1 很重要?

有一种感觉,我想得到一些东西..

San*_*ker 31

代码位于 中_spin_lock_contested_spin_lock_quick当其他人试图获取锁时调用该代码:

count = atomic_fetchadd_int(&spin->counta, 1);
if (__predict_false(count != 0)) {
    _spin_lock_contested(spin, ident, count);
}
Run Code Online (Sandbox Code Playgroud)

如果没有比赛,那么count(之前的值)应该是0,但它不是。该count值作为参数传递给_spin_lock_contested作为value参数。这value然后与检查if从OP:

/*
 * WARNING! Caller has already incremented the lock.  We must
 *      increment the count value (from the inline's fetch-add)
 *      to match.
 *
 * Handle the degenerate case where the spinlock is flagged SHARED
 * with only our reference.  We can convert it to EXCLUSIVE.
 */
if (value == (SPINLOCK_SHARED | 1) - 1) {
    if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED | 1, 1))
        return;
}
Run Code Online (Sandbox Code Playgroud)

请记住,这value是 的先前值spin->counta,而后者已经增加了 1,我们期望spin->counta相等value + 1(除非在此期间发生了某些变化)。

因此,检查 if spin->counta == SPINLOCK_SHARED | 1( 的前提atomic_cmpset_int)对应于检查 if value + 1 == SPINLOCK_SHARED | 1,它可以重写为value == (SPINLOCK_SHARED | 1) - 1(同样,如果在此期间没有任何变化)。

虽然value == (SPINLOCK_SHARED | 1) - 1可以重写为value == SPINLOCK_SHARED,但它保持原样,以阐明比较的意图(即将增加的先前值与测试值进行比较)。

或者哦。答案似乎是:为了清晰和代码一致性。


Gui*_*ean 10

我认为目标可能是忽略最低有效位:

  • 如果用二进制表示的 SPINLOCK_SHARED 是 xxx0 -> 结果是 xxx0
  • 如果 SPINLOCK_SHARED = xxx1 -> 结果也是 xxx0

使用位掩码表达式也许会更清楚?

  • @Lundin Linux 内核不能免除可理解的编码实践。恰恰相反。 (9认同)
  • 这就是代码的作用,但问题是*为什么*您要对没有设置最低有效位的已定义常量执行此操作? (8认同)
  • @SanderDeDycker 因为 Linux 内核? (4认同)
  • @Qix如果你这么说的话。我是 Linux 的忠实粉丝,直到我浏览了代码并阅读了内核编码风格文档。现在我与 Linux 计算机保持 10 米的安全距离。 (2认同)

小智 1

只是为了清楚起见才这样做的,仅此而已。这是因为atomic_fetchadd_int()(例如sys/spinlock2.h)返回加法/减法之前的值,并且该值被传递给_spin_lock_contested()

请注意,C 编译器完全预先计算所有常量表达式。事实上,当过程在这些参数中传递常量时,编译器甚至可以根据使用传入过程参数的条件优化内联代码。这就是为什么 sys/lock.h 中的 lockmgr() 内联有一个 case 语句......因为整个 case 语句将被优化并转变成对适当函数的直接调用。

此外,在所有这些锁定函数中,原子操作的开销使所有其他计算相形见绌两个或三个数量级。

-马特