首页 > 系统相关 >linux 内核 --- 关抢占/关中断,临界区

linux 内核 --- 关抢占/关中断,临界区

时间:2023-10-19 23:45:31浏览次数:27  
标签:lock enable irq local --- preempt 内核 linux spin

 

preempt_disable()
local_irq_disable()/local_irq_save(flags)
spin_lock()
spin_lock_irq()/spin_lock_irqsave(lock, flags)

spin_lock()会调用preempt_disable() 导致本核的抢占调度被关闭(preempt_disable函数实际增加preempt_count来达到此效果),spin_lock_irq()是local_irq_disable()+preempt_disable()的合体。

 

local_irq_disable()/local_irq_save()的disable和save版的唯一区别是,要不要保存CPU对中断的屏蔽状态。

spin_lock_irq()/spin_lock_irqsave(lock, flags)的唯一区别是,要不要保存CPU对中断的屏蔽状态。

 

Kernel的代码明确显示,执行抢占调度的时候,会同时检测“non-zero preempt_count or interrupts are disabled”:

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())   static __always_inline int preempt_count(void) {     return READ_ONCE(current_thread_info()->preempt_count); }

 

 对于ARM处理器而言,判断irqs_disabled(),其实就是判断CPSR中的IRQMASK_I_BIT是否被设置。

最开始列出的所有函数,都能关闭本核的抢占调度。因为,无论是preempt_count计数状态,还是中断被关闭,都会导致kernel认为无法抢占!

 

既然都关闭了抢占,那么区别在哪里呢?

我们看两段代码,假设下面的代码都发生在NICE为0的普通进程:

preempt_disable()

xxx(1)

preempt_enable()

和

local_irq_disable()

xxx(2)

local_irq_enable()

首先,xxx(1)和xxx(2)里面,都是不可以抢占的。一个搞定了preempt_count,一个搞定了中断。

但是假设xxx(1)内唤醒了一个高优先级的RT任务,那么在preempt_enable()的时刻,直接就是一个抢占点,这个时候,发生schedule,高优先级RT任务进来跑;假设xxx(2)内唤醒了一个高优先级的RT任务,那么在local_irq_enable()的时刻,不是一个抢占点,高优先级RT的任务必须等待下一个抢占点。下一个抢占点,可能是时钟tick处理返回、中断返回、软中断结束、yield()等等多种情况。

在preempt_enable()中,会执行一次preempt_schedule():

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

#define __preempt_schedule() preempt_schedule()

 

而local_irq_enable()只是单纯的开启CPU对中断的响应,对于ARM而言,它就是:

#define arch_local_irq_enable arch_local_irq_enable
static inline void arch_local_irq_enable(void)
{
    asm volatile(
        "    cpsie i            @ arch_local_irq_enable"
        :
        :
        : "memory", "cc");
}

再来看大boss,spin_lock_irq是同时调用了preempt_disable和local_irq_disable:

static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
{
    local_irq_disable();
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

而对应的spin_unlock_irq()则同时调用了local_irq_enable()和preempt_enable():

static inline void __raw_spin_unlock_irq(raw_spinlock_t *lock)
{
    spin_release(&lock->dep_map, _RET_IP_);
    do_raw_spin_unlock(lock);
    local_irq_enable();
    preempt_enable();
}

为何preempt_enable()比local_irq_enable()后发生呢?如果代码顺序是这样的:

preempt_enable()

local_irq_enable()

第一句preempt_enable()想执行抢占调度的话,即便调用了preempt_schedule(),但是由于IRQ还是关门的,preempt_schedule()函数会立即返回,所以无法抢占;后一句local_irq_enable()不会执行抢占调度。所以,如果这么干的话,

spin_lock_irq()

xxx(3)

spin_unlock_irq()

如果xxx(3)唤醒了高优先级的RT,在spin_unlock_irq()的时刻,将无法直接抢占!

还好,真正的顺序是:

local_irq_enable()

preempt_enable()

所以,在spin_unlock_irq()的时刻,RT进程就换入执行了。

看小一点的boss,spin_lock():

spin_lock()

xxx(4)

spin_unlock()

如果xxx(4)唤醒了RT进程,在spin_unlock()的时刻,会立即抢入。因为spin_unlock()会调用preempt_enable()。

 

而抢占又杀了谁?

理论上,关抢占,并没有彻底的关闭调度器,因为进程还是可以主动地sleep:

 

上述代码,在spin_lock的区间里面,调用了msleep(),这个时候,不属于抢占,Linux还是会pick下一个task来跑(不是通过preempt_schedule线程切换么?preempt_schedule由于关抢占直接返回

不过这样的代码,一般在后期蕴藏着巨大的风险,导致后期的莫名崩溃。所以呢,实际的工程里面,我们是严格地禁止的。

 

建议大家打开Kernel里面的config里面的DEBUG_ATOMIC_SLEEP,一旦出现这种情况,让kernel去汇报错误。

这种情况下,kernel检测到有人在atomic上下文里面执行可能睡眠的行为,会直接报执行的栈回溯。

 

标签:lock,enable,irq,local,---,preempt,内核,linux,spin
From: https://www.cnblogs.com/god-of-death/p/17775993.html

相关文章

  • IL编制器 --- Fody
    介绍这个项目的名称“Fody”来源于属于织巢鸟科(Ploceidae)的小鸟(Fody),本身意义为编织。核心Fody引擎的代码库地址:https://github.com/Fody/FodyGithub上是这样介绍的:Fody是一个用于织制.NET程序集的可扩展工具。它允许在构建过程中作为一部分来操纵程序集的中间语言(IL),这需......
  • Python用KNN(K-近邻)回归、分类、异常值检测预测房价、最优K值选取、误差评估可视化
    全文链接:https://tecdat.cn/?p=33917原文出处:拓端数据部落公众号KNN是一种非参数学习算法,这意味着它不会对底层数据做出任何假设。这是一个非常有用的特性,因为大多数客户的数据并不真正遵循任何理论假设,例如线性可分性,均匀分布等等。何时应使用KNN?假设您想要租一间公寓并最近......
  • [刷题笔记] [算法学习笔记]树上差分 -- Luogu P3128
    DescriptionProblem:https://www.luogu.com.cn/problem/P3128FJ给他的牛棚的\(N\)个隔间之间安装了\(N-1\)根管道,隔间编号从\(1\)到\(N\)。所有隔间都被管道连通了。FJ有\(K\)条运输牛奶的路线,第\(i\)条路线从隔间\(s_i\)运输到隔间\(t_i\)。一条运输路线会给......
  • 莫能沛--华经情报网
    1、定义洗护用品是指清洁和修饰皮肤、身体、头髮和口腔的产品。主要包括洗发露、沐浴露、洗手液、手工皂、牙膏、洗面奶等产品。2、分类洗护用品按用途可分为头部洗护用品、浴室洗护用品、清洁洗护用品等;按使用人群可分为婴儿洗护用品、成人洗护用品、孕妇洗护用品等。  ......
  • 编辑器Scene视图扩展 - Handles.Slider2D
    效果 #ifUNITY_EDITORusingUnityEditor;usingUnityEngine;publicclassTestSceneGUIWindow:EditorWindow{[MenuItem("MyTools/TestSceneGUIWindow")]publicstaticvoidShowWindow(){EditorWindow.GetWindow<TestSceneGU......
  • R语言改进的K-Means(K-均值)聚类算法分析股票盈利能力和可视化|附代码数据
    全文链接:http://tecdat.cn/?p=32418原文出处:拓端数据部落公众号大量数据中具有"相似"特征的数据点或样本划分为一个类别。聚类分析提供了样本集在非监督模式下的类别划分。人们在投资时总期望以最小的风险获取最大的利益,面对庞大的股票市场和繁杂的股票数据,要想对股票进行合理......
  • 蓝桥云课--各种环境的使用
    学习界面左边栏是实验教学内容和功能区,包含:实验步骤、实验报告和讨论等。右边栏为实验环境区域,包含:实验环境和工具栏等。如果要开始实验,需要点击启动右边的实验环境,然后按照左边实验步骤的指示,一步步完成实验。实验环境实验环境指的是我们在启动实验环境后面对的操作界面,目前,蓝桥云......
  • Epoque: Practical End-to-End Verifiable Post-Quantum-Secure E-Voting
    Abstract—Theultimategoalinmodernsecuree-votingistoenableeveryonetoverifywhetherthefinalelectionresultcorrectlyreflectsthevoteschosenbythe(human)voters,withoutexposinghoweachindividualvoted.Thesefundamentalsecurityproper......
  • 编辑器扩展 - Scene视图
    扩展Scene视图的几种方式1)注册SceneView.duringSceneGui委托(Unity2018及之前版本是SceneView.onSceneGUIDelegate)#ifUNITY_EDITORusingUnityEditor;publicclassTestSceneGUIWindow:EditorWindow{[MenuItem("MyTools/TestSceneGUIWindow")]publicstatic......
  • 2023-10-19 闲话
    如果你非常感兴趣这些内容,请你仔细辨认【数据删除】的含义。你猜猜第二个自然段是怎么造出来的。我好像也不是很清楚。初三有一个课间我从教室走出去上厕所,快上课了走得很急,拐角的地方【数据删除】撞到了我,随便说了两句你没事吧。然后我去厕所【数据删除】回教室。因为是同桌所......