为啥“barrier()”足以禁用或启用抢占?

Posted

技术标签:

【中文标题】为啥“barrier()”足以禁用或启用抢占?【英文标题】:Why a "barrier()" is enough for disabling or enabling the preemption?为什么“barrier()”足以禁用或启用抢占? 【发布时间】:2016-02-25 04:43:33 【问题描述】:

从Linux内核代码,我可以看到preempt_enable()preempt_disable()除了barrier()之外什么都不是:

#define preempt_disable()       barrier()

#define preempt_enable()        barrier()

我无法理解。为什么只需 barrier() 就足以禁用或启用抢占?

【问题讨论】:

【参考方案1】:

因为您没有使用可抢占式内核。 不过,用户空间进程是可抢占的。

检查是否在内核配置中设置了 CONFIG_PREEMPT_VOLUNTARY。

看 What is preemption / What is a preemtible kernel? What is it good for?

从https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable/+/386afc91144b36b42117b0092893f15bc8798a80%5E!/开始添加屏障

【讨论】:

【参考方案2】:

在 Kernel v4.3 中,preempt_enable 的正确定义是 here:

#define preempt_enable() \
do  \
    barrier(); \
    if (unlikely(preempt_count_dec_and_test())) \
           __preempt_schedule(); \
 while (0) 

preempt_disable 同样是here:

#define preempt_disable() \
do  \
     preempt_count_inc(); \
     barrier(); \
 while (0)

preempt_enable 在启用抢占之前插入优化屏障,preempt_disable 在抢占计数器增加后插入屏障。然而,根据comment,当不涉及抢占,而只是保护被抢占区域的障碍时。

EDIT:分别在 UP 和 non-preempt 中,自旋锁和抢占 禁用/启用点被完全剔除,因为没有 可以达到预期的并发性的常规代码 防止。

但是,虽然没有可以导致调度的常规代码,但我们 最终有一些特殊的(字面意思!)代码可以做到这一点, 我们需要确保永远不会进入关键 编译器的区域。

特别是 get_user() 和 put_user() 通常实现为 内联 asm 语句(即使内联 asm 可以调用 指令离线调用),显然会导致页面错误 结果是 IO。如果该内联汇编已安排到 在抢占安全(或自旋锁保护)代码区域的中间,我们 明显输了。

现在,诚然,这非常不太可能真正发生,并且 我们还没有看到与此相关的实际错误示例。但部分 正是因为它很难触发,而由此产生的错误是如此 微妙的,我们应该格外小心才能做到这一点。

因此,请确保即使禁用了抢占,我们也不必这样做 生成任何实际的代码来明确告诉系统我们在 一个禁止抢占的区域,我们至少需要告诉编译器不要 在关键区域周围移动东西。 Source

【讨论】:

preempt_disable的实现需要CONFIG_PREEMPT_COUNT开启,This(#define preempt_disable() barrier())是CONFIG_PREEMPT_COUNT关闭时的版本。 补充上面的描述,内核可以通过两种方式编译,即抢占模式和非抢占模式。 (我不会考虑始终存在的用户空间抢占)。 Kconfig [source] (lxr.free-electrons.com/source/kernel/Kconfig.preempt) 控制抢占的启用/禁用。当我们启用 CONFIG_PREEMPT 时,CONFIG_PREEMPT_COUNT 会自动启用(参考 kconfig.preempt)。当 PREEMPT 被禁用时,调用将替换为 barrier(),它只是将写入刷新到 DRAM。

以上是关于为啥“barrier()”足以禁用或启用抢占?的主要内容,如果未能解决你的问题,请参考以下文章

为啥启用/禁用 A20 线

为啥 UIBarButtonItem 默认是禁用的?

为啥 MPI_Barrier 在 C++ 中会导致分段错误

powershell 启用或禁用网络绑定(即禁用IPv4或启用IPv6)

启用或禁用通知颤动

是否启用或禁用了 CORS?