как {} while(0) работает в макросе?

Хотя эта тема много раз обсуждалась на этом форуме и на всех других форумах, все же у меня есть сомнения. Пожалуйста помоги.

Как работает макрос do{} while(0) в ядре Linux? Например,

#define preempt_disable()    do { } while (0)

Как отключить вытеснение?

#define might_resched()    do { } while (0)

Как это перепланируется?

Точно так же я видел макросы для блокировок мьютексов и других. Как это помогает? Я понимаю следующую проблему, но не примеры выше.

#define foo(x)    do { do something } while(0)

Изменить:

Как насчет следующего кода для rt_mutex_lock?

/**
 * rt_mutex_lock - lock a rt_mutex
 *
 * @lock: the rt_mutex to be locked
 */
void __sched rt_mutex_lock(struct rt_mutex *lock)
{
        might_sleep();
        rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, 0, rt_mutex_slowlock);
}
EXPORT_SYMBOL_GPL(rt_mutex_lock);


/*
 * debug aware fast / slowpath lock,trylock,unlock
 *
 * The atomic acquire/release ops are compiled away, when either the
 * architecture does not support cmpxchg or when debugging is enabled.
 */

static inline int rt_mutex_fastlock(struct rt_mutex *lock, 
    int state, int detect_deadlock, int (*slowfn)(struct rt_mutex *lock, 
    int state, struct hrtimer_sleeper *timeout, int detect_deadlock))
{
        if (!detect_deadlock && likely(rt_mutex_cmpxchg(lock, NULL, current))) {
                rt_mutex_deadlock_account_lock(lock, current);
                return 0;
        } else{
                return slowfn(lock, state, NULL, detect_deadlock);
        }
}

Я запутался, потому что rt_mutex_deadlock_account_lock определяется в двух местах ядра:

In kernel/rtmutex-debug.c:

void rt_mutex_deadlock_account_lock(struct rt_mutex *lock, 
    struct task_struct *task)
{
    //....
}

In kernel/rtmutex.h:

#define rt_mutex_deadlock_account_lock(m, t) do { } while (0)

В новом ядре 2.6.35.4 в драйвере i2c rt_mutex_lock(&adap->bus_lock); заменил mutex_lock(). Как это блокируется тогда?


person iSegFault    schedule 22.09.2010    source источник
comment
мне кажется, что он переопределяет эту функцию, чтобы ничего не делать.   -  person mpen    schedule 22.09.2010
comment
@Mark: Звучит убедительно. ravspratapsingh: Правильно ли мы поняли, что для двух верхних утверждений между фигурными скобками действительно ничего нет? Или вы просто упростили код таким образом?   -  person chiccodoro    schedule 22.09.2010
comment
возможный дубликат Какая польза от do while( 0) когда мы определяем макрос?   -  person paxdiablo    schedule 22.09.2010
comment
@paxdiablo: «возможный дубликат» не так уж близок; в нем обсуждается do ... while (0), но там код имеет действия внутри тела цикла, в отличие от здесь. Так что это несколько другое.   -  person Jonathan Leffler    schedule 22.09.2010
comment
Для меня совершенно ясно, что у авторов ядра была какая-то причина хотеть, чтобы макрос расширялся до чего-то большего, чем просто полностью пустое выражение, но его расширение до нуля или просто до комментария кажется столь же полезным на первый взгляд. В любом случае, это заставило ОП усомниться в этом при чтении исходников ядра, и поэтому я согласен с @Jonathan, что это больше связано с отсутствием тела, чем с тем, что означает do{}while(0).   -  person RBerteig    schedule 22.09.2010
comment
@chiccodoro: это исходный фрагмент кода из ядра, и ничего не было изменено. Меня беспокоит то, что когда do-while(0) ничего не делает, то как он на самом деле блокируется.   -  person iSegFault    schedule 22.09.2010
comment
На самом деле дубликат близок (на мой взгляд, но я всего лишь одна ячейка в рое). Идея состоит в том, чтобы иметь возможность поместить preempt_disable() в любой момент кода, чтобы предоставить инструкцию, которая ничего не делает.   -  person paxdiablo    schedule 22.09.2010


Ответы (3)


@Kragen ответил, для чего предназначена конструкция do...while - в основном она делает макрос более безопасным в использовании.

Однако я не думаю, что это отвечает на вопрос «как это работает?»:

#define preempt_disable()    do { } while (0)

Макрос определен так, чтобы ничего не делать. Почему вы хотите ничего не делать?

  • В некоторых случаях вы хотите использовать макрос в качестве заполнителя для выполнения каких-либо действий. Например, вы можете написать код в одной системе, где «вытеснение» не является проблемой, но вы знаете, что код может быть перенесен в систему, где «вытеснение» требует специальной обработки. Таким образом, вы используете макрос везде, где он нужен второй системе (чтобы обработку было легко включить позже), но для первой системы вы затем определяете этот макрос как пустой макрос.

  • В некоторых случаях вам может понадобиться сделать что-то вроде задачи, состоящей из разных частей (например, START_TABLE(); TABLE_ENTRY(1); TABLE_ENTRY(2); END_TABLE();). Это делает хорошую чистую четкую реализацию вашей таблицы. Но затем вы обнаружите, что на самом деле вам не нужен макрос END_TABLE(). Чтобы сохранить чистоту клиентского кода, вы оставляете макрос определенным и просто определяете его так, чтобы он ничего не делал. Таким образом, все ваши таблицы имеют END_TABLE, и код становится легче читать.

  • Аналогичный случай может возникнуть с двумя состояниями (включено/отключено), где одно состояние требует, чтобы макрос что-то делал, а другое состояние просто происходит по умолчанию, поэтому реализация одного из них «пуста» — вы все равно используете макрос, потому что он делает клиентский код легче понять, потому что в нем явно указаны места, где что-то включено или отключено.

person Jason Williams    schedule 22.09.2010
comment
если do {} while (0) ничего не делает. Из-за этого компилятор генерирует мусорные инструкции или цикл будет оптимизирован? - person Zingam; 21.01.2016
comment
В наши дни у компиляторов есть очень хорошие оптимизаторы, поэтому крайне маловероятно, что вы найдете такой, который тратит время на такой очевидный пустой случай, как этот. - person Jason Williams; 22.01.2016

См. эту ссылку, чтобы получить лучшее объяснение, чем я мог бы дать.

person Justin    schedule 22.09.2010
comment
+1 Просто чтобы показать один уровень косвенности, первая цитата там отвечает на вопрос: (от Дэйва Миллера) Пустые операторы дают предупреждение от компилятора, поэтому вы видите #define FOO do { } while(0). - person Jens Gustedt; 22.09.2010
comment
@Justin и... ссылка мертва. Если вы можете выкопать зеркало, добавьте соответствующую информацию в свой ответ. - person dandan78; 08.02.2019

IIRC использует do-while в макросах, чтобы они больше походили на обычный вызов функции; есть некоторые тонкие синтаксические проблемы, связанные с операторами без фигурных скобок и тому подобными вещами. Без do-while макрос может выглядеть как обычный вызов функции, но работать будет по-другому.

Я предполагаю, что в этом случае эти макросы используются, поэтому некоторые вызовы функций компилируются в ничто; похоже, что это может быть то, что вы получите, если CONFIG_PREEMPT не было установлено, поэтому определенные части ядра, которые необходимы только для вытеснения, просто исчезают без него. Таким образом, эти циклы не отключают вытеснение или перепланирование чего-либо; в исходном коде ядра будет другое определение (вероятно, реальная функция).

person Peter    schedule 22.09.2010
comment
Спасибо, я понял, как работает это вытеснение, но все еще не понимаю блокировку мьютекса. Пожалуйста, посмотрите мой ответ о rt_mutex_lock - person iSegFault; 22.09.2010
comment
Я предполагаю, что блокировка мьютекса чем-то похожа - есть реальная реализация и фиктивная, которая ничего не делает. Предположительно, в некоторых случаях используется фиктивный, если ядро ​​настроено так, что в нем никогда нет необходимости - может быть, если CONFIG_SMP не включен? - person Peter; 24.09.2010