(x | y) - y dlaczego nie może to być po prostu x lub nawet `x | 0`

47

Czytałem kod jądra i w jednym miejscu zobaczyłem wyrażenie w środku if instrukcji, takie jak

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

gdzie SPINLOCK_SHARED = 0x80000000 jest predefiniowaną stałą.

Zastanawiam się, dlaczego potrzebujemy (SPINLOCK_SHARED | 1) - 1 - do konwersji typu? wynikiem wyrażenia byłoby 80000000 - to samo co 0x80000000, prawda? jednak dlaczego ORing 1 i odejmowanie 1 ma znaczenie?

Mam wrażenie, że brakuje mi czegoś ...

RaGa__M
źródło
3
# zdefiniować SPINLOCK_SHARED 0x80000000
RaGa__M,
1
Podejrzewam, że nie ma powodu. Możliwe, że skopiuj i wklej. Czy możesz dodać, gdzie dokładnie to znalazłeś (która wersja jądra, który plik itp.).
Sander De Dycker,
2
Ten sam plik kodu źródłowego również zawiera if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1)).
Eric Postpischil,
2
Potem myślę, że musimy zapytać autora, dlaczego to się zmieniło.
funnydman,

Odpowiedzi:

1

Zrobiono to w ten sposób dla jasności, to wszystko. Jest tak, ponieważ atomic_fetchadd_int () (np. Sys / spinlock2.h) zwraca wartość PRIOR do dodawania / odejmowania, a ta wartość jest przekazywana do _spin_lock_contested ()

Zauważ, że kompilator C całkowicie wstępnie oblicza wszystkie wyrażenia stałe. W rzeczywistości kompilator może nawet zoptymalizować wbudowany kod w oparciu o warunki warunkowe, które wykorzystują argumenty procedury przekazanej, gdy procedury są przekazywane w tych argumentach stałymi. To dlatego funkcja lockmgr () wbudowana w sys / lock.h ma instrukcję case .... ponieważ cała instrukcja case zostanie zoptymalizowana i przekształci się w bezpośrednie wywołanie odpowiedniej funkcji.

Ponadto we wszystkich tych funkcjach blokowania narzut operacji atomowej przewyższa wszystkie inne obliczenia o dwa lub trzy rzędy wielkości.

-Matt

Matthew Dillon
źródło
Ta odpowiedź pochodzi od autora !!!
RaGa__M
31

Znaleziono kod _spin_lock_contested, który jest wywoływany, _spin_lock_quickgdy ktoś inny próbuje uzyskać blokadę:

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

Jeśli nie ma konkursu, to count(poprzednia wartość) powinna być 0, ale tak nie jest. Ta countwartość jest przekazywana jako parametr _spin_lock_contestedjako valueparametr. Jest valueto następnie sprawdzane za pomocą ifz PO:

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

Pamiętając o tym, że valuejest to poprzednia wartość spin->counta, a ta ostatnia została już zwiększona o 1, spodziewamy spin->countasię równości value + 1(chyba że w międzyczasie coś się zmieniło).

Zatem sprawdzenie, czy spin->counta == SPINLOCK_SHARED | 1(warunek wstępny atomic_cmpset_int) odpowiada sprawdzeniu, czy value + 1 == SPINLOCK_SHARED | 1można to przepisać jakovalue == (SPINLOCK_SHARED | 1) - 1 (ponownie, jeśli w międzyczasie nic się nie zmieniło).

Chociaż value == (SPINLOCK_SHARED | 1) - 1można go przepisać jako value == SPINLOCK_SHARED, pozostawia się tak, jak jest, aby wyjaśnić cel porównania (tj. Porównać zwiększoną poprzednią wartość z wartością testową).

Lub iow. odpowiedź wydaje się brzmieć: dla jasności i spójności kodu.

Sander De Dycker
źródło
Dzięki za odpowiedź, wszystko oprócz (SPINLOCK_SHARED | 1) - 1 części jest zrozumiałe i value == SPINLOCK_SHAREDjest to również moja myśl, ponieważ sprawdzamy, czy poprzednia wartość ma ustawioną flagę współdzieloną. Jeśli tak, zamień blokadę w wyłączność .........
RaGa__M
1
@RaGa__M: celem ifkontroli jest sprawdzenie, czy value + 1(która powinna być taka sama, jak spin->countagdyby nic się nie zmieniło w międzyczasie) jest równa SPINLOCK_SHARED | 1. Jeśli wypiszesz ifczek jako value == SPINLOCK_SHARED, zamiar ten nie jest jasny i znacznie trudniej byłoby zrozumieć, co oznacza czek. Utrzymanie obu SPINLOCK_SHARED | 1i - 1wyraźnie w ifkontroli jest celowe.
Sander De Dycker
Ale w rzeczywistości powoduje zamieszanie.
RaGa__M,
Dlaczego nie if (value + 1 == (SPINLOCK_SHARED | 1) )?
Pablo H,
Cóż ... to po prostu mogłoby być value & SPINLOCK_SHAREDbardziej czytelne.
RaGa__M,
10

Myślę, że celem jest prawdopodobnie zignorowanie najmniej znaczącego fragmentu:

  • Jeśli SPINLOCK_SHARED wyrażony w formacie binarnym to xxx0 -> wynikiem jest xxx0
  • Jeśli SPINLOCK_SHARED = xxx1 -> wynikiem jest również xxx0

byłoby chyba bardziej przejrzyste użycie wyrażenia nieco maskującego?

Guillaume Petitjean
źródło
8
To właśnie robi kod, ale pytanie brzmi: dlaczego miałbyś to zrobić dla zdefiniowanej stałej, która nie ma ustawionego najmniej znaczącego bitu?
Sander De Dycker,
4
@SanderDeDycker Ponieważ jądro Linuksa?
Lundin,
9
@Lundin Jądro linuksa nie jest zwolnione ze zrozumiałych praktyk kodowania. Wręcz przeciwnie.
Qix - MONICA MISTREATED 19.12.19
2
@Qix Jeśli tak mówisz. Byłem wielkim fanem Linuksa, dopóki nie zajrzałem do kodu i nie przeczytałem dokumentu stylu kodowania jądra. Obecnie zachowuję 10 metrów bezpiecznej odległości od komputerów z systemem Linux.
Lundin,
2
@Qix Nie, raczej oceniam go na podstawie kodu źródłowego ...
Lundin,
4

Efekt

(SPINLOCK_SHARED | 1) - 1

ma zapewnić, że bit wyniku niskiego rzędu zostanie wyczyszczony przed porównaniem z value. Zgadzam się, że wydaje się to raczej bezcelowe, ale najwyraźniej bit niskiego rzędu ma szczególne zastosowanie lub znaczenie, które nie jest widoczne w tym kodzie, i myślę, że musimy założyć, że deweloperzy mieli dobry powód, aby to zrobić. Ciekawym pytaniem byłoby - czy ten sam wzorzec ( | 1) -1) jest używany w całej bazie kodu, na którą patrzysz?

Bob Jarvis - Przywróć Monikę
źródło
2

To zaciemniony sposób pisania trochę maski. Czytelna wersja: value == (SPINLOCK_SHARED & ~1u).

Lundin
źródło
5
Tak, ale dlaczego . OP pyta, dlaczego tak jest, jeśli SPINLOCK_SHAREDjest znaną stałą. Jeśli po prostu testują SPINLOCK_SHAREDobecność w masce, dlaczego nie if (value & SPINLOCK_SHARED)?
Qix - MONICA MISTREATED 19.12.19
4
value == (SPINLOCK_SHARED & ~1u)nie jest równoważne, ponieważ value == (SPINLOCK_SHARED | 1) - 1działa, nawet jeśli typ SPINLOCK_SHAREDjest szerszy niż unsigned.
Eric Postpischil,
4
Szczerze mówiąc, nie jestem pewien, czy to & ~1ujest bardziej zrozumiałe. Pomyślałem o sugestii & 0xFFFFFFFEw mojej odpowiedzi, ale zdałem sobie sprawę, że to też nie jest do końca jasne. Twoja sugestia ma jednak zaletę zwięzłości. :-)
Bob Jarvis - Przywróć Monikę
6
@Lundin: Nie wiemy, że tak będzie 0x80000000. OP stwierdził, że jest zdefiniowany #define SPINLOCK_SHARED 0x80000000, ale może być w środku, #if…#endifa inna definicja jest używana w innych okolicznościach, lub autor tego kodu mógł chcieć, aby działał, nawet jeśli definicja jest edytowana lub kod jest skompilowany z innymi nagłówkami, które zdefiniuj to inaczej. Niezależnie od tego dwa fragmenty kodu same w sobie nie są równoważne.
Eric Postpischil,
2
@ BobJarvis-ReinstateMonica Jest to znacznie bardziej zrozumiałe dla osób, które codziennie pracują z operatorami bitowymi. Mieszanie bitowe ze zwykłą arytmetyką jest mylące.
Lundin
0

Większość tego jest wykonywana w celu obsługi kilku dodatkowych przypadków. Na przykład w tym przypadku mówimy, że SPINLOCK_SHAREDnie może być 1:

int SPINLOCK_SHARED = 0x01

int res = (SPINLOCK_SHARED | 1) - 1 // 0
funnydman
źródło
2
Miałoby to sens, ale chociaż tak naprawdę nie jest jasne z pytania, wydaje się, że SPINLOCK_SHAREDjest zdefiniowaną stałą, a testowaną zmienną jest value. W tym przypadku tajemnica pozostaje.
Roberto Caboni,
Dzięki za odpowiedź, ale myślę, że w oryginalnym przypadku SPINLOCK_SHARED nie ma wartości 0x01, zachowałeś tę | 1) - 1część, gdy SPINLOCK_SHARED trzyma 0x80000000wpływ, jaki miałby wpływ | 1) - 1?
RaGa__M,
Jedynym powodem, dla którego mogłem wymyślić, było to, że chcieli się wystrzegać przed SPINLOCK_SHAREDzmianą w przyszłości. Ale to wcale nie jest jasne. Chciałbym napisać do deweloperów jądra i poprosić o komentarz wyjaśniający lub o zmianę wyrażenia w celu samodokumentowania.
Qix - MONICA MISTREATED 19.12.19