【问题标题】:how does do{} while(0) work in macro?{} while(0) 如何在宏中工作?
【发布时间】:2011-04-15 13:47:06
【问题描述】:

虽然这个话题在这个论坛和所有其他论坛上已经讨论过很多次了,但我仍然有疑问。请帮忙。

do{} while(0) in 宏在 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 在内核中的两个地方定义:

kernel/rtmutex-debug.c:

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

kernel/rtmutex.h:

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

在 i2c 驱动程序中的新内核 2.6.35.4 中 rt_mutex_lock(&adap->bus_lock); 已替换 mutex_lock()。那这个怎么锁呢?

【问题讨论】:

  • 在我看来它重新定义了该功能什么都不做。
  • @Mark:听起来很有说服力。 ravspratapsingh:对于上面的两个语句,我们是否理解正确,大括号之间真的没有任何内容?还是您只是这样简化了代码?
  • @paxdiablo:“可能的重复”并不是那么接近;它讨论了do ... while (0),但代码在循环体内有动作——不像这里。所以这有点不同。
  • 对我来说很清楚,内核作者有一些理由希望宏扩展为不仅仅是一个完全空的表达式,但使其扩展为空或只是一个注释似乎同样有用乍一看。无论如何,它导致 OP 在阅读内核源代码时对其提出质疑,因此我同意 @Jonathan 的观点,即更多的是缺少正文,而不是 do{}while(0) 的含义。
  • @chiccodoro :这是来自内核的原始代码片段,没有任何修改。我担心的是,当 do-while(0) 什么都不做时,它实际上是如何锁定的。

标签: c do-while


【解决方案1】:

请参阅this link 以获得比我能给出的更好的解释。

【讨论】:

  • +1 只是为了弹出一个间接级别,那里的第一个引用回答了这个问题:(来自 Dave Miller)空语句会发出来自编译器的警告,所以这就是您看到 #define FOO do { } while(0) 的原因。
  • @justin 和...链接已失效。如果你能挖到镜像,请在你的答案中添加相关信息。
【解决方案2】:

IIRC 在宏中使用 do-while 是为了让它们看起来更像是一个普通的函数调用;围绕 unbraced if 语句和类似的东西存在一些微妙的语法问题。如果没有 do-while,宏可能看起来像一个普通的函数调用,但工作方式会有所不同。

我猜想在这种情况下使用了这些宏,因此某些函数调用编译为空;如果没有设置CONFIG_PREEMPT,看起来这可能就是你得到的,所以内核的某些部分只需要抢占,没有它就会消失。所以这些循环不会禁用抢占或重新安排任何事情;在内核源代码的其他地方会有另一个定义(可能是一个真正的函数)。

【讨论】:

  • 谢谢,我明白了这个抢占的东西是如何工作的,但仍然对互斥锁感到困惑。请看我关于 rt_mutex_lock 的回复
  • 我认为互斥锁是类似的——有一个“真实”的实现和一个什么都不做的“虚拟”实现。大概在某些情况下,如果内核被配置为永远不需要它,则使用虚拟的 - 也许如果未启用 CONFIG_SMP?
【解决方案3】:

@Kragen 已经回答了 do...while 构造的用途 - 它基本上使宏的使用更加安全。

但是,我认为它不能回答“这是如何工作的?”的问题:

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

宏被定义为什么都不做。为什么你什么都不想做?

  • 在某些情况下,您希望使用宏作为占位符来执行某些操作。例如,您可能在一个“抢占”不是问题的系统上编写代码,但您知道代码可能被移植到“抢占”需要一些特殊处理的系统。因此,您在第二个系统需要它的任何地方都使用宏(以便以后轻松启用处理),但对于第一个系统,您随后将该宏定义为空白宏。

  • 在某些情况下,您可能想要执行由不同部分组成的任务(例如 START_TABLE(); TABLE_ENTRY(1); TABLE_ENTRY(2); END_TABLE();)。这使您的表的实现非常干净清晰。但随后您发现您实际上并不需要 END_TABLE() 宏。为了保持客户端代码整洁,您保留宏定义,并简单地将其定义为什么都不做。这样,您所有的表都有一个 END_TABLE 并且代码更易于阅读。

  • 1234563使用宏是因为它使客户端代码更容易理解,因为它明确说明了启用或禁用事物的位置。

【讨论】:

  • 如果do {} while (0) 什么都不做。编译器会因此产生垃圾指令还是循环被优化掉?
  • 现在的编译器有非常好的优化器,所以你不太可能找到一个在这样一个明显的空壳上浪费时间的优化器。
猜你喜欢
  • 1970-01-01
  • 2013-10-07
  • 2021-04-14
  • 1970-01-01
  • 2012-05-30
  • 1970-01-01
  • 2015-04-30
  • 1970-01-01
  • 2012-03-18
相关资源
最近更新 更多