(x | y)-yなぜ単純にxまたは `x | 0`


47

私はカーネルコードを読んでいて、ある場所で次のifようなステートメント内の式を見ました

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

ここで、SPINLOCK_SHARED = 0x80000000事前定義された定数です。

なぜ(SPINLOCK_SHARED | 1) - 1型変換が必要なのでしょうか。式の結果は80000000になります-0x80000000と同じですよね?それでも、なぜORing 1とSubtracting 1が重要なのですか?

何かを手に入れられないような気がします。


3
#define SPINLOCK_SHARED 0x80000000
RaGa__M

1
理由はないと思います。おそらくコピー・ペーストのことです。これを見つけた場所(カーネルのバージョン、ファイルなど)を追加してください。
Sander De Dycker

2
ここでは、github.com
DragonFlyBSD

2
同じソースコードファイルにもが含まれていますif (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1))
Eric Postpischil

2
次に、なぜそれが変更されたのかを著者に尋ねる必要があると思います。
funnydman

回答:


1

分かりやすくするためにそのように行われただけです。これは、atomic_fetchadd_int()(例:sys / spinlock2.h)が値PRIORを加算/減算に返し、その値が_spin_lock_contested()に渡されるためです。

Cコンパイラはすべての定数式を完全に事前計算することに注意してください。実際、コンパイラーは、渡されたプロシージャー引数に定数が渡されるときに、渡されたプロシージャー引数を使用する条件に基づいて、インラインコードを最適化することもできます。sys / lock.hのインラインのlockmgr()にcaseステートメントがあるのはこのためです...そのcaseステートメント全体が最適化され、適切な関数への直接呼び出しに展開されるためです。

また、これらすべてのロック関数では、アトミックopsのオーバーヘッドにより、他のすべての計算が2桁または3桁小さくなります。

-マット


この回答は著者からです!!!
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_contestedvalueパラメーターとしてに渡されます。value次に、これはifOPから確認されます。

/*
 * 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 | 1value == (SPINLOCK_SHARED | 1) - 1(再度、当面何も変更されていない場合)と書き換えられるifのチェックに対応します。

ながらvalue == (SPINLOCK_SHARED | 1) - 1のように書き換えることができvalue == SPINLOCK_SHARED、それは比較の意図を明確にすること、であるとして残っている(すなわち。テスト値とインクリメント前の値を比較します)。

またはまぁ。答えは:明快さとコードの一貫性のためです。


ご回答をお寄せいただきありがとうございます、以外のすべて(SPINLOCK_SHARED | 1) - 1の部分は理解し、value == SPINLOCK_SHARED私の考えは、私たちが.........排他的にロックを回し、はいset.if前の値がいるかどうかを共有フラグをチェックしているので、あまりにもある
RaGa__M

1
@RaGa__M:ifチェックの目的は、value + 1spin->countaその間に何も変更されていない場合と同じ値である必要がある)がと等しいかどうかをチェックすることですSPINLOCK_SHARED | 1ifチェックをと書くとvalue == SPINLOCK_SHARED、この意図は明確ではなく、チェックの意味を理解するのは非常に難しくなります。両方SPINLOCK_SHARED | 1- 1明示的にif確認することは意図的なものです。
Sander De Dycker

しかし、それは実際に混乱を引き起こしています。
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
それがコードが行うことですが、問題は最下位ビットが設定されていない定義済み定数に対してなぜそれを行うのですか?
Sander De Dycker

4
@SanderDeDycker Linuxカーネルだから?
ランディン

9
@Lundin Linuxカーネルは、理解できるコーディング方法から除外されていません。まったく逆です。
Qix-モニカは

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-モニカは

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あり、別の定義が他の状況で使用されているか、またはこのコードの作成者が、定義が編集されたり、コードが別の方法で定義します。いずれにしても、2つのコードはそれ自体では同等ではありません。
Eric Postpischil

2
@ BobJarvis-ReinstateMonica毎日ビット単位の演算子を使用する人にとっては、はるかに明確です。通常の算術とビット単位で混合すると混乱を招きます。
ランディン

0

このような方法は、いくつかの追加のケースを処理するために行われます。たとえば、この場合は、SPINLOCK_SHARED1にはできません。

int SPINLOCK_SHARED = 0x01

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

2
それは理にかなっていますが、実際には質問から明確ではありませんSPINLOCK_SHAREDが、定義された定数であり、テストされた変数はであるように聞こえますvalue。この場合、ミステリーは残ります。
Roberto Caboni

お返事ありがとうございます。ただし、元のケースではSPINLOCK_SHAREDが0x01ではないと思います。 | 1) - 10x80000000何の影響を与えるかを保持している とき部分| 1) - 1か?
RaGa__M

私が考えることができる唯一の理由は、彼らSPINLOCK_SHAREDが将来変更されるのを防ごうと思ったからです。しかし、それはまったく明確ではありません。私はカーネル開発者に書き込み、明確なコメントを付けるか、式が自己文書化されるように再配置するように依頼します。
Qix-モニカは
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.