(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 имеют значение?

Такое ощущение, что мне не хватает чего-то получить ..

RaGa__M
источник
3
#define SPINLOCK_SHARED 0x80000000
RaGa__M
1
Я подозреваю, что нет никаких причин. Возможно, вещь копирования-вставки. Не могли бы вы добавить, где именно вы это нашли (какая версия какого ядра, какой файл и т. Д.).
Сандер Де
2
Тот же файл исходного кода также содержит if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1)).
Эрик Постпишил
2
Тогда я думаю, что мы должны спросить автора, почему это было изменено.
funnydman

Ответы:

1

Это было просто сделано для ясности, вот и все. Это потому, что atomic_fetchadd_int () (например, в sys / spinlock2.h) возвращает значение PRIOR для сложения / вычитания, и это значение передается в _spin_lock_contested ()

Обратите внимание, что компилятор C полностью предварительно вычисляет все константные выражения. Фактически, компилятор может даже оптимизировать встроенный код на основе условных выражений, которые используют переданные аргументы процедур, когда процедурам передаются константы в этих аргументах. Вот почему встроенный в sys / lock.h файл lockmgr () имеет оператор case .... потому что весь оператор case будет оптимизирован и превращен в прямой вызов соответствующей функции.

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

Матф

Мэтью Диллон
источник
Этот ответ от автора !!!
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, оно оставлено как есть, чтобы уточнить цель сравнения (т. Е. Сравнить увеличенное предыдущее значение с тестовым значением).

Или да. Ответ, кажется, для ясности и последовательности кода.

Сандер Де Дайкер
источник
Спасибо за ваш ответ, все, кроме (SPINLOCK_SHARED | 1) - 1части, понятно, и value == SPINLOCK_SHAREDэто тоже моя мысль, потому что мы проверяем, имеет ли предыдущее значение флаг общего набора set.if да, превращаем блокировку в исключительную .........
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 Нет, я скорее оцениваю это на основе его исходного кода ...
Лундин
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.
Эрик Постпишил
4
Честно говоря, я не уверен, что & ~1uэто понятнее. Я думал предложить & 0xFFFFFFFEв своем ответе, но понял, что это также не совсем понятно. Ваше предложение действительно имеет преимущество краткости, однако. :-)
Боб Джарвис - Восстановить Монику
6
@Lundin: мы не знаем, что это будет 0x80000000. OP заявил, что он определен с #define SPINLOCK_SHARED 0x80000000, но это может быть внутри, #if…#endifи другое определение используется в других обстоятельствах, или автор этого кода мог бы заставить его работать, даже если определение отредактировано или код скомпилирован с другими заголовками, которые определить это по-другому. Независимо от этого, две части кода не являются эквивалентными сами по себе.
Эрик Постпищил
2
@ BobJarvis-ReinstateMonica Это гораздо понятнее людям, которые работают с побитовыми операторами каждый день. Смешивание по битам с обычной арифметикой сбивает с толку.
Лундин
0

Скорее всего, это сделано для обработки нескольких дополнительных случаев. Например, в этом случае мы говорим, что SPINLOCK_SHAREDне может быть 1:

int SPINLOCK_SHARED = 0x01

int res = (SPINLOCK_SHARED | 1) - 1 // 0
funnydman
источник
2
Это имело бы смысл, но, хотя на самом деле это не ясно из вопроса, звучит так, как будто SPINLOCK_SHAREDэто определенная константа, а проверенная переменная - это value. В этом случае загадка остается.
Роберто Кабони
Спасибо за ваш ответ, но я думаю, что в оригинальном случае SPINLOCK_SHARED не 0x01, вы сохранили | 1) - 1часть, когда SPINLOCK_SHARED удерживает, 0x80000000каково будет влияние | 1) - 1?
RaGa__M
Единственная причина, по которой я мог придумать, заключается в том, что они хотели остерегаться SPINLOCK_SHAREDизменений в будущем. Но это не совсем понятно. Я хотел бы написать разработчикам ядра и попросить дать поясняющий комментарий или переставить выражение, чтобы оно самодокументировалось.
Qix - МОНИКА БЫЛА НЕПРАВИЛЬНОЙ