(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 і віднімання 1 мають значення?

У мене таке відчуття, ніби я сумую, щоб щось дістати ..


3
#define SPINLOCK_SHARED 0x80000000
RaGa__M

1
Я підозрюю, що немає причин. Можливо, річ копіювати та вставляти. Чи можете ви додати, де саме ви це знайшли (яка версія ядра, який файл тощо).
Сандер Де


2
Цей самий файл вихідного коду також містить if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1)).
Eric Postpischil

2
Тоді я думаю, що нам потрібно запитати автора, чому це було змінено.
смішник

Відповіді:


1

Це було зроблено саме так для наочності, ось і все. Це тому, що atomic_fetchadd_int () (наприклад, sys / spinlock2.h) повертає значення PRIOR на додавання / віднімання, і це значення передається _spin_lock_conested ()

Зауважте, що компілятор C повністю попередньо обчислює всі постійні вирази. Насправді компілятор може навіть оптимізувати вбудований код на основі умовних умов, які використовують аргументи переданих процедур, коли процедури передаються константами в цих аргументах. Ось чому lockmgr (), вбудований в sys / lock.h, має випадок випадку .... тому що весь випадок справи буде оптимізований та перейде в прямий виклик відповідної функції.

Крім того, у всіх цих замикаючих функціях накладні атомні оси карликовують усі інші обчислення на два-три порядки.

-Матт


Ця відповідь від автора !!!
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допомогою ОП:

/*
 * 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, залишається таким, як є, для уточнення наміру порівняння (тобто порівняння нарощеного попереднього значення з тестовим значенням).

Або iow. начебто відповідь: для ясності та послідовності коду.


Дякую за вашу відповідь, все, крім (SPINLOCK_SHARED | 1) - 1частини, зрозуміло, і value == SPINLOCK_SHAREDмоя думка теж, тому що ми перевіряємо, чи попереднє значення має спільний прапор set.i якщо так, перетворіть замок у ексклюзивний .........
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) )?
Пабло Н

Ну .... це просто може бути, value & SPINLOCK_SHAREDщо є більш читабельним.
RaGa__M

10

Я думаю, що ціль - це, мабуть, ігнорувати найнижчий значущий біт:

  • Якщо SPINLOCK_SHARED, виражений у двійковій формі, є xxx0 -> результат - xxx0
  • Якщо SPINLOCK_SHARED = xxx1 -> результат також xxx0

було б, мабуть, зрозуміліше використовувати експрес-маску?


8
Це те, що робить код, але питання полягає в тому, чому б ви зробили це для визначеної константи, яка не має найменшого значущого біта?
Сандер Де

4
@SanderDeDycker Тому що ядро ​​Linux?
Лундін

9
@Lundin Ядро Linux не звільняється від зрозумілих практик кодування. Зовсім навпаки.
Qix - МОНІКА ПОМИЛИЛА

2
@Qix Якщо ви так говорите. Я був великим шанувальником Linux, поки не зазирнув до коду і не прочитав документ у стилі кодування ядра. У наші дні я тримаю 10-метрову безпечну відстань до комп'ютерів Linux.
Лундін

2
@Qix Nah Я досить суджу про це на основі його вихідного коду ...
Lundin

4

Ефект від

(SPINLOCK_SHARED | 1) - 1

полягає у тому, щоб забезпечити очищення бітового результату низького порядку перед порівнянням з value. Я погоджуюся, що це здається досить безглуздим, але, мабуть, біт низького порядку має певне використання або значення, що не видно в цьому коді, і я думаю, що ми повинні припустити, що у розробників були вагомі причини для цього. Цікавим було б питання - чи використовується той самий шаблон ( | 1) -1) у всій кодовій базі, яку ви дивитесь?


2

Це заплутаний спосіб написання трохи маски. Читається версія: value == (SPINLOCK_SHARED & ~1u).


5
Так, але чому . ОП запитує, чому це було б так, якщо 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і інше визначення використовується в інших обставинах, або автор цього коду міг би передбачити його роботу, навіть якщо визначення було відредаговано або код складено з іншими заголовками, які визначте це по-різному. Незважаючи на те, два фрагменти коду самі по собі не рівноцінні.
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 - МОНІКА ПОМИЛИЛА
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.