简体   繁体   English

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

[英](x | y) - y why can't it simply be x or even `x | 0`

I was reading a kernel code, and in one place I've seen an expression inside if statement like我正在阅读内核代码,并且在一个地方我看到了if语句中的一个表达式,例如

if (value == (SPINLOCK_SHARED | 1) - 1) {
         ............
}

where SPINLOCK_SHARED = 0x80000000 is a predefined constant.其中SPINLOCK_SHARED = 0x80000000是预定义的常量。

I wonder why do we need (SPINLOCK_SHARED | 1) - 1 - for type conversion purpose?我想知道为什么我们需要(SPINLOCK_SHARED | 1) - 1 - 用于类型转换目的? the result of the expression would be 80000000-- same as 0x80000000, is it not?表达式的结果将是 80000000-- 与 0x80000000 相同,不是吗? yet, why ORing 1 and Subtracting 1 matters?然而,为什么 ORing 1 和 Subtracting 1 很重要?

Have a feeling like I am missing to get something..有一种感觉,我想得到一些东西..

The code is found in _spin_lock_contested , which is called from _spin_lock_quick when someone else is attempting to obtain the lock :代码位于_spin_lock_contested ,当其他人试图获取锁时,它会从_spin_lock_quick调用:

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

If there's no contest, then count (the previous value) should be 0 , but it isn't.如果没有比赛,那么count (前一个值)应该是0 ,但它不是。 This count value is passed as parameter to _spin_lock_contested as the value parameter.count数值作为参数传递给_spin_lock_contested作为value参数。 This value is then checked with the if from the OP :然后使用来自 OP 的if检查该value

/*
 * 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;
}

Keeping in mind that value is the previous value of spin->counta , and the latter has already been incremented by 1, we expect spin->counta to equal value + 1 (unless something has changed in the meantime).请记住valuespin->counta的先前值,而后者已经增加了 1,我们期望spin->counta等于value + 1 (除非在此期间发生了某些变化)。

So, checking if spin->counta == SPINLOCK_SHARED | 1因此,检查是否spin->counta == SPINLOCK_SHARED | 1 spin->counta == SPINLOCK_SHARED | 1 (the precondition of the atomic_cmpset_int ) corresponds to checking if value + 1 == SPINLOCK_SHARED | 1 spin->counta == SPINLOCK_SHARED | 1atomic_cmpset_int的前提)对应于检查value + 1 == SPINLOCK_SHARED | 1 value + 1 == SPINLOCK_SHARED | 1 , which can be rewritten as value == (SPINLOCK_SHARED | 1) - 1 (again, if nothing has changed in the meantime). value + 1 == SPINLOCK_SHARED | 1 ,可以重写为value == (SPINLOCK_SHARED | 1) - 1 (同样,如果在此期间没有任何变化)。

While value == (SPINLOCK_SHARED | 1) - 1 could be rewritten as value == SPINLOCK_SHARED , it's left as is, to clarify the intent of the comparison (ie. to compare the incremented previous value with the test value).虽然value == (SPINLOCK_SHARED | 1) - 1可以重写为value == SPINLOCK_SHARED ,但它保持原样,以阐明比较的意图(即将增加的先前值与测试值进行比较)。

Or iow.或者哦。 the answer appears to be : for clarity and code consistency.答案似乎是:为了清晰和代码一致性。

I think the goal is probably to ignore the lowest significant bit:我认为目标可能是忽略最低有效位:

  • If SPINLOCK_SHARED expressed in binary is xxx0 -> result is xxx0如果用二进制表示的 SPINLOCK_SHARED 是 xxx0 -> 结果是 xxx0
  • If SPINLOCK_SHARED = xxx1 -> result is also xxx0如果 SPINLOCK_SHARED = xxx1 -> 结果也是 xxx0

would have been perhaps clearer to use a bit mask expression ?使用位掩码表达式也许会更清楚?

The effect of的效果

(SPINLOCK_SHARED | 1) - 1

is to ensure that the low-order bit of the result is cleared prior to the comparison with value .是为了确保在与value比较之前清除结果的低位。 I agree that it seems rather pointless but apparently the low-order bit has a particular usage or meaning which is not apparent in this code, and I think we have to assume that the devs had a good reason for doing this.我同意这似乎毫无意义,但显然低阶位具有特定用法或含义,在此代码中并不明显,我认为我们必须假设开发人员有充分的理由这样做。 An interesting question would be - is this same pattern ( | 1) -1 ) used throughout the codebase you're looking at?一个有趣的问题是 - 您正在查看的整个代码库中是否使用了相同的模式( | 1) -1 )?

It's an obfuscated way of writing a bit mask.这是一种编写位掩码的混淆方式。 Readable version: value == (SPINLOCK_SHARED & ~1u) .可读版本: value == (SPINLOCK_SHARED & ~1u)

It was just done that way for clarity, that's all.这样做只是为了清楚起见,仅此而已。 It's because atomic_fetchadd_int() (in eg sys/spinlock2.h) returns the value PRIOR to the addition/subtraction, and that value is passed to _spin_lock_contested()这是因为 atomic_fetchadd_int()(例如在 sys/spinlock2.h 中)返回加法/减法之前的值,并将该值传递给 _spin_lock_contested()

Note that the C compiler completely pre-calculates all constant expressions.请注意,C 编译器会完全预先计算所有常量表达式。 In fact, the compiler can even optimize-out inlined code based on conditionals that use passed-in procedure arguments when the procedures are passed constants in those arguments.事实上,编译器甚至可以根据使用传入过程参数的条件来优化内联代码,当过程在这些参数中传递常量时。 This is why the lockmgr() inline in sys/lock.h has a case statement.... because that entire case statement will be optimized out and devolve into a direct call to the appropriate function.这就是为什么 sys/lock.h 中的内联 lockmgr() 有一个 case 语句......因为整个 case 语句将被优化并转化为对适当函数的直接调用。

Also, in all of these locking functions the overhead of the atomic ops dwarf all other calculations by two or three orders of magnitude.此外,在所有这些锁定函数中,原子操作的开销使所有其他计算相形见绌两个或三个数量级。

-Matt -马特

Most like this is done to handle several additional cases.最喜欢这样做是为了处理几个额外的情况。 For example, in this case, we say that SPINLOCK_SHARED can not be 1:例如,在这种情况下,我们说SPINLOCK_SHARED不能为 1:

int SPINLOCK_SHARED = 0x01

int res = (SPINLOCK_SHARED | 1) - 1 // 0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM