(x | y) - y perché non può essere semplicemente x o anche `x | 0`


47

Stavo leggendo un codice del kernel, e in un posto ho visto un'espressione dentro una ifdichiarazione come

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

dove SPINLOCK_SHARED = 0x80000000è una costante predefinita.

Mi chiedo perché ne abbiamo bisogno (SPINLOCK_SHARED | 1) - 1- ai fini della conversione del tipo? il risultato dell'espressione sarebbe 80000000, uguale a 0x80000000, no? ma perché ORing 1 e Sottraendo 1 sono importanti?

Sento che mi manca per ottenere qualcosa ..


3
#define SPINLOCK_SHARED 0x80000000
RaGa__M

1
Sospetto non ci sia motivo. Forse una cosa copia-incolla. Potresti aggiungere esattamente dove hai trovato questo (quale versione di quale kernel, quale file, ecc.).
Sander De Dycker,


2
Lo stesso file di codice sorgente contiene anche if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1)).
Eric Postpischil,

2
Quindi penso che dobbiamo chiedere all'autore perché è stato modificato.
funnydman,

Risposte:


1

È stato fatto così per chiarezza, tutto qui. È perché atomic_fetchadd_int () (ad es. Sys / spinlock2.h) restituisce il valore PRIOR all'addizione / sottrazione e quel valore viene passato a _spin_lock_contested ()

Si noti che il compilatore C pre-calcola completamente tutte le espressioni costanti. In effetti, il compilatore può persino ottimizzare il codice inline basato su condizionali che usano argomenti di procedure passate quando alle procedure vengono passate costanti in tali argomenti. Questo è il motivo per cui lockmgr () inline in sys / lock.h ha un'istruzione case ... perché l'intera istruzione case sarà ottimizzata e devoluta in una chiamata diretta alla funzione appropriata.

Inoltre, in tutte queste funzioni di blocco il sovraccarico delle operazioni atomiche riduce tutti gli altri calcoli di due o tre ordini di grandezza.

-Opaco


Questa risposta è dell'autore !!!
RaGa__M,

31

Il codice si trova in _spin_lock_contested, che viene chiamato da _spin_lock_quickquando qualcun altro sta tentando di ottenere il blocco:

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

Se non ci sono contest, allora count(il valore precedente) dovrebbe essere 0, ma non lo è. Questo countvalore viene passato come parametro a _spin_lock_contestedcome valueparametro. Questo valueviene quindi verificato con ifdall'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;
}

Tenendo presente che valueè il valore precedente di spin->counta, e quest'ultimo è già stato incrementato di 1, ci aspettiamo spin->countache sia uguale value + 1(a meno che qualcosa non sia cambiato nel frattempo).

Quindi, verificare se spin->counta == SPINLOCK_SHARED | 1(la condizione preliminare di atomic_cmpset_int) corrisponde a verificare se value + 1 == SPINLOCK_SHARED | 1, che può essere riscritto come value == (SPINLOCK_SHARED | 1) - 1(di nuovo, se nel frattempo non è cambiato nulla).

Mentre value == (SPINLOCK_SHARED | 1) - 1potrebbe essere riscritto come value == SPINLOCK_SHARED, è lasciato così com'è, per chiarire l'intento del confronto (cioè per confrontare il valore precedente incrementato con il valore del test).

O iow. la risposta sembra essere: per chiarezza e coerenza del codice.


Grazie per la tua risposta, tutto tranne la (SPINLOCK_SHARED | 1) - 1parte è comprensibile ed value == SPINLOCK_SHAREDè anche il mio pensiero, perché stiamo verificando se il valore precedente ha impostato il flag condiviso. Se sì, trasforma il blocco in esclusivo .........
RaGa__M

1
@RaGa__M: l'intento del ifcontrollo è verificare se value + 1(che dovrebbe avere lo stesso valore di spin->countase nel frattempo non sia cambiato nulla) è uguale SPINLOCK_SHARED | 1. Se scrivi l' ifassegno come value == SPINLOCK_SHARED, questo intento non è chiaro e sarebbe molto più difficile capire cosa significhi l'assegno. Mantenere entrambi SPINLOCK_SHARED | 1ed - 1esplicitamente il ifcontrollo è intenzionale.
Sander De Dycker,

Ma in realtà sta causando confusione.
RaGa__M,

Perché no if (value + 1 == (SPINLOCK_SHARED | 1) )?
Pablo H,

Bene ... potrebbe essere semplicemente value & SPINLOCK_SHAREDpiù leggibile.
RaGa__M del

10

Penso che l'obiettivo sia probabilmente di ignorare il bit significativo più basso:

  • Se SPINLOCK_SHARED espresso in binario è xxx0 -> il risultato è xxx0
  • Se SPINLOCK_SHARED = xxx1 -> il risultato è anche xxx0

sarebbe stato forse più chiaro usare un'espressione con un po 'di maschera?


8
Questo è ciò che fa il codice, ma la domanda è: perché lo faresti per una costante definita che non ha il bit meno significativo impostato?
Sander De Dycker,

4
@SanderDeDycker Perché il kernel Linux?
Lundin,

9
@Lundin Il kernel di Linux non è esente da pratiche di codifica comprensibili. Piuttosto il contrario.
Qix - MONICA È STATA MISTREATA il

2
@Qix Se lo dici tu. Ero un grande fan di Linux fino a quando non ho dato una sbirciatina al codice e letto il documento di stile di codifica del kernel. Oggi mantengo una distanza di sicurezza di 10 metri dai computer Linux.
Lundin,

2
@Qix Nah, lo sto piuttosto giudicando in base al suo codice sorgente ...
Lundin,

4

L'effetto di

(SPINLOCK_SHARED | 1) - 1

è assicurarsi che il bit di ordine inferiore del risultato venga cancellato prima del confronto con value. Concordo sul fatto che sembra piuttosto inutile, ma apparentemente il bit di basso ordine ha un uso o un significato particolare che non è evidente in questo codice, e penso che dobbiamo supporre che gli sviluppatori abbiano una buona ragione per farlo. Una domanda interessante sarebbe: questo stesso modello ( | 1) -1) è usato in tutta la base di codice che stai guardando?


2

È un modo offuscato di scrivere una maschera. Versione Leggibile: value == (SPINLOCK_SHARED & ~1u).


5
Sì, ma perché . L'OP chiede perché questo sarebbe il caso se SPINLOCK_SHAREDè una costante nota. Se stanno semplicemente testando la SPINLOCK_SHAREDpresenza in una maschera, perché no if (value & SPINLOCK_SHARED)?
Qix - MONICA È STATA MISTREATA il

4
value == (SPINLOCK_SHARED & ~1u)non è equivalente perché value == (SPINLOCK_SHARED | 1) - 1funziona anche se il tipo di SPINLOCK_SHAREDè più ampio di unsigned.
Eric Postpischil,

4
Onestamente, non sono sicuro che & ~1usia più chiaro. Ho pensato di suggerire & 0xFFFFFFFEnella mia risposta, ma ho capito che anche questo non è molto chiaro. Il tuo suggerimento ha però il vantaggio della brevità. :-)
Bob Jarvis - Ripristina Monica il

6
@Lundin: non sappiamo che lo sarà 0x80000000. OP ha dichiarato che è definito #define SPINLOCK_SHARED 0x80000000, ma che potrebbe essere all'interno #if…#endife una definizione diversa viene utilizzata in altre circostanze, oppure l'autore di questo codice avrebbe potuto farlo funzionare anche se la definizione viene modificata o il codice viene compilato con altre intestazioni che definirlo diversamente. Indipendentemente da ciò, i due pezzi di codice non sono equivalenti da soli.
Eric Postpischil,

2
@ BobJarvis-ReinstateMonica È molto più chiaro per le persone che lavorano ogni giorno con operatori bit per bit. Mischiare bit a bit con l'aritmetica regolare è fonte di confusione.
Lundin,

0

La maggior parte di questi viene eseguita per gestire diversi casi aggiuntivi. Ad esempio, in questo caso, diciamo che SPINLOCK_SHAREDnon può essere 1:

int SPINLOCK_SHARED = 0x01

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

2
Avrebbe senso ma, sebbene in realtà non sia chiaro dalla domanda, sembra che SPINLOCK_SHAREDsia una costante definita e la variabile testata lo sia value. In questo caso il mistero rimane.
Roberto Caboni,

Grazie per la tua risposta, ma penso che nel caso originale SPINLOCK_SHARED non sia 0x01, hai mantenuto la | 1) - 1parte, quando SPINLOCK_SHARED ha tenuto 0x80000000l'impatto di quale sarebbe | 1) - 1?
RaGa__M,

L'unica ragione che mi viene in mente è che volevano evitare di SPINLOCK_SHAREDessere cambiati in futuro. Ma non è affatto chiaro. Vorrei scrivere agli sviluppatori del kernel e chiedere un commento di chiarimento da fare o che l'espressione fosse riorganizzata in modo tale da auto-documentarsi.
Qix - MONICA È STATA MISTREATA il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.