(x | y) - y warum kann es nicht einfach x oder sogar `x | sein? 0`

47

Ich habe einen Kernel-Code gelesen und an einer Stelle habe ich einen Ausdruck in einer ifAnweisung wie gesehen

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

wo SPINLOCK_SHARED = 0x80000000ist eine vordefinierte Konstante.

Ich frage mich, warum wir brauchen (SPINLOCK_SHARED | 1) - 1- für die Typkonvertierung? Das Ergebnis des Ausdrucks wäre 80000000 - genau wie 0x80000000, nicht wahr? Warum ist ORing 1 und Subtrahieren von 1 wichtig?

Ich habe das Gefühl, dass mir etwas fehlt.

RaGa__M
quelle
3
#define SPINLOCK_SHARED 0x80000000
RaGa__M
1
Ich vermute, es gibt keinen Grund. Möglicherweise eine Copy-Paste-Sache. Könnten Sie hinzufügen, wo genau Sie dies gefunden haben (welche Version von welchem ​​Kernel, welche Datei usw.).
Sander De Dycker
2
Die gleiche Quellcodedatei enthält auch if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1)).
Eric Postpischil
2
Dann müssen wir wohl den Autor fragen, warum es geändert wurde.
Funnydman

Antworten:

1

Es wurde nur aus Gründen der Klarheit so gemacht, das ist alles. Dies liegt daran, dass atomic_fetchadd_int () (z. B. sys / spinlock2.h) den Wert PRIOR für die Addition / Subtraktion zurückgibt und dieser Wert an _spin_lock_contested () übergeben wird.

Beachten Sie, dass der C-Compiler alle konstanten Ausdrücke vollständig vorberechnet. Tatsächlich kann der Compiler sogar Inline-Code basierend auf Bedingungen optimieren, die übergebene Prozedurargumente verwenden, wenn die Prozeduren Konstanten in diesen Argumenten übergeben werden. Aus diesem Grund hat die Inline lockmgr () in sys / lock.h eine case-Anweisung .... da diese gesamte case-Anweisung optimiert und in einen direkten Aufruf der entsprechenden Funktion umgewandelt wird.

Bei all diesen Verriegelungsfunktionen stellt der Overhead der Atomoperationen alle anderen Berechnungen um zwei oder drei Größenordnungen in den Schatten.

-Matt

Matthew Dillon
quelle
Diese Antwort ist vom Autor !!!
RaGa__M
31

Der Code befindet sich in _spin_lock_contested, der aufgerufen wird, _spin_lock_quickwenn jemand anderes versucht, die Sperre zu erhalten:

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

Wenn es keinen Wettbewerb gibt, sollte count(der vorherige Wert) sein 0, ist es aber nicht. Dieser countWert wird als Parameter an _spin_lock_contestedals valueParameter übergeben. Dies valuewird dann mit dem ifvom OP überprüft :

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

Wenn wir bedenken, dass dies valueder vorherige Wert von spin->countaist und dieser bereits um 1 erhöht wurde, erwarten wir, dass spin->countaer gleich ist value + 1(es sei denn, etwas hat sich in der Zwischenzeit geändert).

Die Überprüfung, ob spin->counta == SPINLOCK_SHARED | 1(die Voraussetzung der atomic_cmpset_int) entspricht der Überprüfung, ob value + 1 == SPINLOCK_SHARED | 1, was umgeschrieben werden kann value == (SPINLOCK_SHARED | 1) - 1(erneut, wenn sich in der Zwischenzeit nichts geändert hat).

Während value == (SPINLOCK_SHARED | 1) - 1es umgeschrieben werden könnte value == SPINLOCK_SHARED, bleibt es unverändert , um die Absicht des Vergleichs zu verdeutlichen (dh den inkrementierten vorherigen Wert mit dem Testwert zu vergleichen).

Oder jetzt. Die Antwort scheint zu sein: aus Gründen der Klarheit und Codekonsistenz.

Sander De Dycker
quelle
Vielen Dank für Ihre Antwort, alles außer (SPINLOCK_SHARED | 1) - 1Teil ist verständlich und value == SPINLOCK_SHAREDist auch mein Gedanke, denn wir prüfen, ob für den vorherigen Wert das Shared-Flag gesetzt wurde. Wenn ja, verwandeln Sie die Sperre in exklusives .........
RaGa__M
1
@RaGa__M: Mit der ifPrüfung soll geprüft werden , ob value + 1(was der gleiche Wert sein sollte, als spin->countahätte sich in der Zwischenzeit nichts geändert) gleich ist SPINLOCK_SHARED | 1. Wenn Sie den ifScheck als schreiben value == SPINLOCK_SHARED, ist diese Absicht nicht klar, und es wäre viel schwieriger herauszufinden, was der Scheck bedeutet. Beides SPINLOCK_SHARED | 1und - 1explizit in der ifKontrolle zu halten, ist beabsichtigt.
Sander De Dycker
Aber es verursacht tatsächlich Verwirrung.
RaGa__M
Warum nicht if (value + 1 == (SPINLOCK_SHARED | 1) )?
Pablo H
Naja .... es könnte einfach sein, value & SPINLOCK_SHAREDwas besser lesbar ist.
RaGa__M
10

Ich denke, das Ziel ist wahrscheinlich, das niedrigstwertige Bit zu ignorieren:

  • Wenn SPINLOCK_SHARED in binär ausgedrückt xxx0 ist -> Ergebnis ist xxx0
  • Wenn SPINLOCK_SHARED = xxx1 -> Ergebnis ist auch xxx0

wäre es vielleicht klarer gewesen, einen Bitmaskenausdruck zu verwenden?

Guillaume Petitjean
quelle
8
Das ist, was der Code tut, aber die Frage ist, warum Sie das für eine definierte Konstante tun würden, für die nicht das niedrigstwertige Bit gesetzt ist?
Sander De Dycker
4
@SanderDeDycker Weil Linux-Kernel?
Lundin
9
@Lundin Der Linux-Kernel ist nicht von verständlichen Codierungspraktiken ausgenommen. Ganz im Gegenteil.
Qix - MONICA wurde am
2
@ Qix Wenn du es sagst. Ich war ein großer Fan von Linux, bis ich mir den Code ansah und das Dokument im Kernel-Codierungsstil las. Heutzutage halte ich einen Sicherheitsabstand von 10 Metern zu Linux-Computern.
Lundin
2
@ Qix Nein, ich beurteile es eher anhand seines Quellcodes ...
Lundin
4

Der Effekt von

(SPINLOCK_SHARED | 1) - 1

soll sicherstellen, dass das niederwertige Bit des Ergebnisses vor dem Vergleich mit gelöscht wird value. Ich stimme zu, dass es ziemlich sinnlos erscheint, aber anscheinend hat das Bit niedriger Ordnung eine bestimmte Verwendung oder Bedeutung, die in diesem Code nicht ersichtlich ist, und ich denke, wir müssen davon ausgehen, dass die Entwickler einen guten Grund dafür hatten. Eine interessante Frage wäre: Wird dasselbe Muster ( | 1) -1) in der gesamten Codebasis verwendet, die Sie betrachten?

Bob Jarvis - Monica wieder einsetzen
quelle
2

Es ist eine verschleierte Art, eine Bitmaske zu schreiben. Lesbare Version : value == (SPINLOCK_SHARED & ~1u).

Lundin
quelle
5
Ja, aber warum . Das OP fragt, warum dies der Fall wäre, wenn SPINLOCK_SHAREDes sich um eine bekannte Konstante handelt. Wenn sie nur testen, ob sie SPINLOCK_SHAREDin einer Maske vorhanden sind, warum nicht if (value & SPINLOCK_SHARED)?
Qix - MONICA wurde am
4
value == (SPINLOCK_SHARED & ~1u)ist nicht gleichwertig, da es value == (SPINLOCK_SHARED | 1) - 1auch dann funktioniert, wenn der Typ SPINLOCK_SHAREDbreiter als ist unsigned.
Eric Postpischil
4
Ehrlich gesagt bin ich mir nicht sicher, ob das & ~1uklarer ist. Ich dachte daran, & 0xFFFFFFFEin meiner Antwort einen Vorschlag zu machen, stellte jedoch fest, dass dies auch nicht wirklich klar ist. Ihr Vorschlag hat jedoch den Vorteil der Kürze. :-)
Bob Jarvis - Reinstate Monica
6
@Lundin: Wir wissen nicht, dass es sein wird 0x80000000. OP hat angegeben, dass es mit definiert ist #define SPINLOCK_SHARED 0x80000000, aber das könnte drin sein #if…#endifund unter anderen Umständen wird eine andere Definition verwendet, oder der Autor dieses Codes könnte beabsichtigt haben, dass es funktioniert, selbst wenn die Definition bearbeitet oder der Code mit anderen Headern kompiliert wird, die definiere es anders. Unabhängig davon sind die beiden Codeteile für sich genommen nicht gleichwertig.
Eric Postpischil
2
@ BobJarvis-ReinstateMonica Es ist viel klarer für Leute, die jeden Tag mit bitweisen Operatoren arbeiten. Das bitweise Mischen mit normaler Arithmetik ist verwirrend.
Lundin
0

Die meisten davon werden durchgeführt, um mehrere zusätzliche Fälle zu behandeln. In diesem Fall sagen wir zum Beispiel, dass SPINLOCK_SHAREDdies nicht 1 sein kann:

int SPINLOCK_SHARED = 0x01

int res = (SPINLOCK_SHARED | 1) - 1 // 0
Funnydman
quelle
2
Es würde Sinn machen, aber obwohl es aus der Frage nicht wirklich klar ist, klingt es so, als wäre SPINLOCK_SHAREDes eine definierte Konstante und die getestete Variable ist value. In diesem Fall bleibt das Geheimnis.
Roberto Caboni
Vielen Dank für Ihre Antwort. Aber ich denke, im Originalfall ist SPINLOCK_SHARED nicht 0x01. Sie haben den | 1) - 1Teil beibehalten , als SPINLOCK_SHARED 0x80000000die Auswirkungen hielt | 1) - 1.
RaGa__M
Der einzige Grund, an den ich denken konnte, war, dass sie sich davor schützen wollten, SPINLOCK_SHAREDin Zukunft verändert zu werden. Aber es ist überhaupt nicht klar. Ich würde an die Kernel-Entwickler schreiben und um eine Klarstellung bitten oder um eine Neuordnung des Ausdrucks, damit er sich selbst dokumentiert.
Qix - MONICA wurde am