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


47

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

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

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

我想知道为什么我们需要(SPINLOCK_SHARED | 1) - 1-用于类型转换?表达式的结果将是80000000-与0x80000000相同,不是吗?但是,“或1”和“减1”为何重要?

感觉好像我想不到的东西..


3
#define SPINLOCK_SHARED 0x80000000
RaGa__M

1
我怀疑没有理由。可能是复制粘贴的东西。您能否添加找到它的确切位置(哪个内核的版本,哪个文件等)。
桑德·戴克

2
在这里,github.com
DragonFlyBSD /

2
相同的源代码文件还包含if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1))
Eric Postpischil

2
然后,我认为我们需要问作者为什么要更改它。
funnydman

Answers:


1

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

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

同样,在所有这些锁定功能中,原子操作的开销使所有其他计算都相差两个或三个数量级。

-马特


这个答案来自作者!!!
RaGa__M

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);
}

如果没有比赛,则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;
}

请记住,这value是的前一个值spin->counta,而后者已经增加了1,我们希望spin->counta等于value + 1(除非与此同时有所变化)。

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

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

还是哇 答案似乎是:为了清楚和代码一致。


感谢您的回复,除了(SPINLOCK_SHARED | 1) - 1part 以外的所有其他内容都是可以理解的,value == SPINLOCK_SHARED也是我的想法,因为我们正在检查先前的值是否已设置了共享标志。如果是,请将锁转换为独占式.........
RaGa__M

1
@RaGa__M:if检查的目的是检查value + 1(其值应该与spin->counta在此期间没有任何变化一样)SPINLOCK_SHARED | 1。如果将if检查写为value == SPINLOCK_SHARED,则此意图尚不清楚,并且要弄清楚检查的含义会困难得多。保持双方SPINLOCK_SHARED | 1- 1在明确if检查是故意的。
桑德·戴克

但这实际上引起了混乱。
RaGa__M

为什么不if (value + 1 == (SPINLOCK_SHARED | 1) )呢?
Pablo H

好吧....它可能value & SPINLOCK_SHARED更容易阅读。
RaGa__M

10

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

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

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


8
这就是代码的作用,但是问题是为什么您要为没有设置最低有效位的已定义常量执行此操作?
桑德·戴克

4
@SanderDeDycker因为Linux内核?
隆丁

9
@Lundin linux内核不能免除可理解的编码实践。恰恰相反。
Qix-蒙尼卡(Monica)

2
@Qix如果你这么说。在浏览代码并阅读内核编码风格文档之前,我一直是Linux的忠实拥护者。如今,我与Linux计算机之间的安全距离为10米。
隆丁

2
@Qix Nah我想根据其源代码对其进行判断...
Lundin

4

的效果

(SPINLOCK_SHARED | 1) - 1

是为了确保在与比较之前清除结果的低位value。我同意这似乎毫无意义,但显然低阶位具有特定用法或含义,而这在代码中并不明显,我认为我们必须假设开发人员有充分的理由这样做。一个有趣的问题是- | 1) -1您正在查看的整个代码库中是否使用了相同的模式()?


2

这是写位掩码的一种混淆方式。可读版本:value == (SPINLOCK_SHARED & ~1u)


5
是的,但是为什么。OP询问如果已知常数,为什么会是这种情况SPINLOCK_SHARED。如果他们只是测试SPINLOCK_SHARED口罩中是否存在,为什么不if (value & SPINLOCK_SHARED)呢?
Qix-蒙尼卡(Monica)

4
value == (SPINLOCK_SHARED & ~1u)不等价,因为value == (SPINLOCK_SHARED | 1) - 1即使的类型SPINLOCK_SHARED比宽,也可以使用unsigned
Eric Postpischil

4
老实说,我不确定这& ~1u是否更清楚。我想& 0xFFFFFFFE在我的答案中提出建议,但意识到这还不是很清楚。不过,您的建议确实具有简洁的优势。:-)
鲍勃·贾维斯

6
@Lundin:我们不知道会这样0x80000000。OP已声明使用进行了定义#define SPINLOCK_SHARED 0x80000000,但可以在内部#if…#endif进行定义,并且在其他情况下可以使用不同的定义,或者即使编辑了定义或使用其他标头编译了该代码,该代码的作者也可能希望它能够正常工作。定义不同。无论如何,这两段代码本身并不等效。
Eric Postpischil,

2
@ BobJarvis-ReinstateMonica对于每天与按位运算符一起工作的人来说,这要清楚得多。按位与常规算术混合会造成混淆。
隆丁

0

这样做最多是为了处理其他几种情况。例如,在这种情况下,我们说SPINLOCK_SHARED不能为1:

int SPINLOCK_SHARED = 0x01

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

2
这是有道理的,但尽管实际上从问题中还不清楚,但这听起来像是SPINLOCK_SHARED已定义的常量,而被测试的变量是value。在这种情况下,雾气依然存在。
罗伯特·卡波尼

感谢您的答复,但是我认为在原始情况下SPINLOCK_SHARED不是0x01,您保留了该| 1) - 1部分,当SPINLOCK_SHARED持有时0x80000000会产生什么影响 | 1) - 1
RaGa__M

我能想到的唯一原因是他们想避免SPINLOCK_SHARED将来被更改。但这还不清楚。我会写信给内核开发人员,并要求做出澄清说明或重新排列表达式,以便它可以自我记录。
Qix-蒙尼卡(Monica)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.