首页 > 其他分享 >preempt_enable()

preempt_enable()

时间:2023-01-07 16:35:21浏览次数:50  
标签:count enable 抢占 中断 preempt 进程

本文记录了对preempt_enable()实现的一些思考。

常见的开启/关闭抢占的接口

spin_lock()

  • 关抢占 (1)
  • 拿锁 (2)

spin_unlock()

  • 释放锁 (1)
  • 开抢占 (2)

spin_lock_irq()

  • 关本地中断 (1)
  • 关抢占 (2)
  • 拿锁 (3)

spin_unlock_irq()

  • 释放锁 (1)
  • 开本地中断 (2)
  • 开抢占 (3)

拿锁时候都是最后一步拿锁,此时关闭抢占/关闭本地中断,确保拿到锁之后不会被本CPU其它进程抢占或者被本CPU中断打断

放锁时候都是第一步就释放锁,然后再去开抢占/本地中断,因为要让其它CPU上等待锁的进程能尽快拿锁。

spin_unlock_irq()中执行preempt_enable()开启抢占时,有可能进行进程调度。


preempt_enable()的实现

(Linux内核版本 4.19)

#ifdef CONFIG_PREEMPT
#define preempt_enable() \
do { \
	barrier(); \
	if (unlikely(preempt_count_dec_and_test())) \   //开抢占时判断是否嫩执行进程抢占切换
		__preempt_schedule(); \
} while (0)

#define preempt_count_dec_and_test() __preempt_count_dec_and_test()

static __always_inline bool __preempt_count_dec_and_test(void)
{
	/*
	 * Because of load-store architectures cannot do per-cpu atomic
	 * operations; we cannot use PREEMPT_NEED_RESCHED because it might get
	 * lost.
	 */
	return !--*preempt_count_ptr() && tif_need_resched();  //允许抢占切换的条件:1.抢占计数为0  2.当前进程TIF_NEED_RESCHED被置位
}

static __always_inline volatile int *preempt_count_ptr(void)
{
	return &current_thread_info()->preempt_count; //这里有点小疑惑,preempt_count保存在struct thread_info ??
}

#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED)

#define test_thread_flag(flag) \
	test_ti_thread_flag(current_thread_info(), flag)

static inline int test_ti_thread_flag(struct thread_info *ti, int flag)
{
	return test_bit(flag, (unsigned long *)&ti->flags);
}
asmlinkage __visible void __sched notrace preempt_schedule(void)
{
	/*
	 * If there is a non-zero preempt_count or interrupts are disabled,
	 * we do not want to preempt the current task. Just return..
	 */
	if (likely(!preemptible()))
		return;

	preempt_schedule_common();
}

#define preemptible()	(preempt_count() == 0 && !irqs_disabled())  //条件1:抢占计数为0  条件2:中断开启

preempt_enable()中当以下条件满足,则执行preempt_schedule()尝试切换进程

  • 当前进程抢占计数preempt_count为0(说明不在中断处理上下文中)
  • 当前进程thread_info中flag的TIF_NEED_RESCHED被置位

preempt_schedule()首先检查是否可以抢占调度,条件如下:

  • 当前进程抢占计数preempt_count为0
  • CPU本地中断为开启状态

preempt_schedule()为什么要求CPU本地中断为开启状态呢?

我的理解是这样的:假设进程A在CPU2上执行local_irq_disable()关掉CPU2的本地中断后,在未调用local_irq_enable()之前,被CPU2的进程B抢占执行,那么此时CPU2的本地中断什么时候开启,我们是不确定的。因为从设计上我们需要确保【谁关闭了中断,那么就由谁来开启中断】的编程模型不被破坏,所以当进程A重新调度到CPU2上执行local_irq_enable()时,CPU2本地中断才被打开。 当然,进程B在CPU2如果执行local_irq_enable()最终仍然会打开CPU2的本地中断。

对进程A呢?如果进程A在CPU2执行时,还没有打开CPU2的本地中断,就被抢占调度随即被调度到CPU3继续执行,那么进程A在CPU3执行local_irq_enable()则直接打开了CPU3的本地中断。有同学可能会问:直接打开CPU3的中断有什么问题吗? 我的想法是这样的:如果我们允许抢占发生时本地中断为关闭状态,那么CPU3上的进程有可能由于同步原因关闭了CPU3的本地中断,一旦进程A切换到CPU3执行,进程A执行local_irq_enable()时直接打开了CPU3的本地中断,这可能会导致CPU3其它进程的同步逻辑错误。

我的理解是这样的:

编程模型是系统设计原则在编程规范中的体现。设计上采用了【谁关闭本地中断,那么由谁来激活本地中断】的原则,因此软件层面的行为即是遵循此设计思想的具体工程实现

不积跬步无以至千里

巨人的肩膀

宋宝华: 是谁关闭了Linux抢占,而抢占又关闭了谁?

https://blog.csdn.net/juS3Ve/article/details/80153974?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.channel_param

标签:count,enable,抢占,中断,preempt,进程
From: https://www.cnblogs.com/ecjtusbs/p/17032916.html

相关文章