从 Linux 内核代码中,我可以看到preempt_enable()
andpreempt_disable()
只是barrier()
:
#define preempt_disable() barrier()
#define preempt_enable() barrier()
我无法理解。为什么只 abarrier()
就足以禁用或启用抢占?
从 Linux 内核代码中,我可以看到preempt_enable()
andpreempt_disable()
只是barrier()
:
#define preempt_disable() barrier()
#define preempt_enable() barrier()
我无法理解。为什么只 abarrier()
就足以禁用或启用抢占?
因为您没有使用可抢占的内核。但是,用户空间进程是可抢占的。
检查内核配置中是否设置了 CONFIG_PREEMPT_VOLUNTARY。
在 Kernel v4.3 中,正确的定义preempt_enable
在这里:
#define preempt_enable() \
do { \
barrier(); \
if (unlikely(preempt_count_dec_and_test())) \
__preempt_schedule(); \
} while (0)
同样 forpreempt_disable
在这里:
#define preempt_disable() \
do { \
preempt_count_inc(); \
barrier(); \
} while (0)
preempt_enable
在启用抢占之前插入优化屏障,并preempt_disable
在抢占计数器增加后插入屏障。但是根据评论,当不涉及抢占而只是保护抢占区域的障碍时。
编辑:分别在 UP 和 non-preempt 中,自旋锁和抢占禁用/启用点被完全排除,因为没有常规代码可以达到它们要保护的并发性。
然而,虽然没有可以导致调度的常规代码,但我们 最终确实有一些可以做到这一点的异常(字面意思!)代码,并且我们需要确保编译器不会将其移动到临界区。
尤其是 get_user() 和 put_user() 通常是作为内联 asm 语句实现的(即使内联 asm 可能随后会发出调用指令来调用外联),显然会导致页面错误和 IO . 如果该内联汇编被安排到抢占安全(或自旋锁保护)代码区域的中间,我们显然会失败。
现在,诚然,这实际上不太可能发生,而且我们还没有看到与此相关的实际错误示例。但部分原因是因为它很难触发,而且由此产生的 bug 非常微妙,我们应该格外小心,以确保它正确无误。
所以确保即使在抢占被禁用的情况下,我们也不必生成任何实际代码来明确告诉系统我们处于抢占禁用区域,我们至少需要告诉编译器不要在临界区。资源