遇到一个BUG: scheduling while atomic: kworker/0:2/370/0x00000002;看了这篇文章BUG: scheduling while atomic 分析,是因为在原子操作上下文或者中断上下文进行了调度引起的。

先看下为什么会打印出这句:

schedule() -> __schedule() -> schedule_debug()
static inline void schedule_debug(struct task_struct *prev)
{
#ifdef CONFIG_SCHED_STACK_END_CHECKif (task_stack_end_corrupted(prev))panic("corrupted stack end detected inside scheduler\n");
#endifif (unlikely(in_atomic_preempt_off())) {__schedule_bug(prev);preempt_count_set(PREEMPT_DISABLED);}rcu_sleep_check();profile_hit(SCHED_PROFILING, __builtin_return_address(0));schedstat_inc(this_rq()->sched_count);
}/** Print scheduling while atomic bug:*/
static noinline void __schedule_bug(struct task_struct *prev)
{/* Save this before calling printk(), since that will clobber it */unsigned long preempt_disable_ip = get_preempt_disable_ip(current);if (oops_in_progress)return;printk(KERN_ERR "BUG: scheduling while atomic: %s/%d/0x%08x\n",prev->comm, prev->pid, preempt_count());debug_show_held_locks(prev);print_modules();if (irqs_disabled())print_irqtrace_events(prev);if (IS_ENABLED(CONFIG_DEBUG_PREEMPT)&& in_atomic_preempt_off()) {pr_err("Preemption disabled at:");print_ip_sym(preempt_disable_ip);pr_cont("\n");}if (panic_on_warn)panic("scheduling while atomic\n");dump_stack();add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
}

也就是 if (unlikely(in_atomic_preempt_off())) 这句成立,就会报这个错误。

#define in_atomic_preempt_off() (preempt_count() != PREEMPT_DISABLE_OFFSET)

preempt_count()  读取 preempt_count,这个成员被用来判断当前进程是否可以被抢占,这个值是一个 task 的 thread info 中的一个成员变量。也就是 preempt_count 被改变不等于 PREEMPT_DISABLE_OFFSET 之后就会报这个错。可能是代码调用 preempt_disable 显式的禁止了抢占,也可能是处于中断上下文等。其中 preempt_disable 和 preempt_enable 成对出现是对 preempt_count 进行加一和减一的操作。

那么 preempt_count 在什么情况下会发生改变呢?

1.原子操作上下文中,比如spin_lock。

spin_lock()->raw_spin_lock()->_raw_spin_lock()->__raw_spin_lock()static inline void __raw_spin_lock(raw_spinlock_t *lock)
{preempt_disable();spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

2.中断上下文中。

handle_IRQ()->__handle_domain_irq()->irq_enter()->__irq_enter()#define __irq_enter()                    \do {                       \account_irq_enter_time(current);   \preempt_count_add(HARDIRQ_OFFSET); \trace_hardirq_enter();         \} while (0)

硬件中断来的时候会调用 handle_IRQ 进中断处理函数,会调用到 preempt_count_add(HARDIRQ_OFFSET),这个函数虽然不是像 preempt_disable 将 preempt_count 加一,但是它过分的是将其加 HARDIRQ_OFFSET(1UL << 16),这个时候显然是不等于 PREEMPT_DISABLE_OFFSET,如果调用 schedule 就会报 bug。

这也同时让我想起之前说为什么中断不能睡眠,网上多数分析的原因都是些主观上的不能睡眠调度的原因,并没有从代码上分析。其中让我夜不能寐一句说:

2.4内核中schedule()函数本身在进来的时候判断是否处于中断上下文:

if(unlikely(in_interrupt()))

BUG();

因此,强行调用schedule()的结果就是内核BUG,但我看2.6.18的内核schedule()的实现却没有这句,改掉了。

其实2.6以上是有实现的,就是上面的 flow 触发。2.4是分别判断 in_atomic 和 in_interrupt (咱也没找到2.4的kernel code,咱就敢说,你找去吧,反正你也找不到),2.6以后将所有不允许调度的情况都集合在 preempt_count 这个变量上,用不同的位来代表不同的情况,具体preempt_count的数据格式可以参考下图:

preemption count 用来记录当前被显式的禁止抢占的次数,这就和代码里一致了:中断中是加 HARDIRQ_OFFSET(1UL << 16) 对应 bit16,原子上下文是加1对应 bit0。

我们再看那句log:

printk(KERN_ERR "BUG: scheduling while atomic: %s/%d/0x%08x\n",prev->comm, prev->pid, preempt_count());

最后会将 preemption count 打印出来,这样就能通过这个值来看是因为在哪种情况下调用 schedule 而导致的 kernel crash,还是内核牛批啊。

所以说也不能老是听大佬说中断不能调睡眠函数又不去想为啥,还是得看代码怎么实现的,它万一有一天客户拿枪顶着我的脑袋让我解释个中原因呢?

我赌你枪里没有子弹!

参考文章:

中断上下文中调度会怎样?

linux kernel的中断子系统之(八):softirq

BUG: scheduling while atomic 分析 and 为什么中断不能睡眠相关推荐

  1. 【Linux】【Kernel】BUG: scheduling while atomic问题分析

    内核模块local_clients用于监听DHCP包,并把主机名字,IP,MAC等信息写到文件/proc/local_clients/local_clients_info,然后webserver解析这 ...

  2. [crash分析]“Kernel panic - not syncing: Aiee, killing interrupt handler”“BUG: scheduling while atomic“

    crash的直接提示信息"Kernel panic - not syncing: Aiee, killing interrupt handler!",不太常见.crash栈也没太多 ...

  3. [Kernel_exception6] BUG: scheduling while atomic

    一.实际问题: 1.开机出现原子上下文调度的bug: [ 6.290494] <1>.(1)[285:init]BUG: scheduling while atomic: init/285 ...

  4. BUG: scheduling while atomic: swapper/0/0/0x00010000

    BUG: scheduling while atomic: swapper/0/0/0x00010000 出现此错误的原因是: 在中断上下文中不能进行进程的调度. 下面用一个例子验证下: 在我们的按键 ...

  5. zynq7000 中断原理分析及IO中断解析

    Zynq 7000 中断分析 文章目录 Zynq 7000 中断分析 GIC及中断机理 Crotex-A9常用汇编指令 Zynq中断控制 GPIO 中断控制 单IP双通道 单IP多位 双IP中断源 G ...

  6. ESP32 外部中断原理分析 GPIO外部中断实战

    ESP32 外部中断原理分析 & GPIO外部中断实战 阅读建议:   有一定Cortex-m架构.Xtensa® 32-bit LX6 架构知识基础. 软件环境 VSCODE-ESP32-I ...

  7. 中断不可睡眠的一些理解

    一.        LINUX中到是有中断还没有完全返回就调用schedule()而睡眠过去的例子.        可以猜是哪里.        我觉得,中断和异常不同,中断是异步的,异常和系统调用是 ...

  8. mcp2515 芯片驱动总线错误BUG的解决方法(主要无法进入中断bug)

    http://blog.renren.com/share/221002615/11483613167 来自张涛的日志 现象:CAN总线在线上设备热插拔或长时间运行后出现总线异常情况,有时不能发送和接收 ...

  9. 再次聊聊游戏测试中的bug:bug层级划分与分析

    声明,本文所说内容仅针对游戏测试,软件测试并不适用. Bug通常是我们测试人员日常处理的最多的工作,在一个游戏中可能存在各种层次的bug,今天我们就简单聊一聊,除了我们日常处理的内容,还有哪些内容可以 ...

最新文章

  1. 2021-08-08概率论与数理统计-第二章
  2. MySQL- In 和 Exists的优化案例讲解
  3. 白话Elasticsearch56-数据建模之 Path Hierarchy Tokenizer 对文件系统进行数据建模以及文件搜索
  4. [css] 请举例说明伪元素 (pseudo-elements) 有哪些用途?
  5. 中文巨量模型“源1.0”的学习优化方法
  6. 使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)——第1部分
  7. 手机安装 Linux 系统教程
  8. 第三方支付接口申请和开发
  9. 程序的本质之一程序编译的详细过程
  10. 用java编写斗兽棋
  11. 3D打印肝模型抢救生命
  12. 如何提高深度学习的泛化能力?
  13. Struts2-052 漏洞复现
  14. 计算机网络ospf实验报告,计算机网络实验报告12_ospf实验
  15. 音视频的相关名词、术语、概念
  16. 结构体+联合体 详解
  17. DevOps/SRE 成长计划
  18. ICPR 2022 | 多模态字幕识别竞赛正式启动!
  19. 中国剩余定理和EXCRT
  20. 免费在线恋爱纪念日、结婚纪念日计算器

热门文章

  1. Unity技术手册 - 初识粒子系统及预览效果【Particle Effect】面板介绍
  2. 使用html+jquery+本地的音乐文件实现了基本的网页音乐播放器
  3. Zero-Shot Graph Relation Prediction through Commonsense Knowledge Integration
  4. 一个机械工程师可以较快的切入到结构工程领域
  5. html百叶窗效果代码,纯CSS 3D百叶窗图像过渡特效
  6. C语言 函数 (库函数 · 自定义函数 · 函数参数 · 函数调用 · 嵌套调用链式访问 · 递归)
  7. 南京工业大学计算机考研难吗,南京工业大学考研难吗?一般要什么水平才可以进入?...
  8. ORM概念,tortoise-orm安装
  9. 年轻人为什么更喜欢真无线蓝牙耳机?2020五款高性价比蓝牙耳机推荐
  10. 全国行政边界json数据echarts地图geojson生成精确到城镇街道-20211208