LINUX软中断-ksoftirqd
admin
2024-05-14 12:45:54
0

前言

在上一篇 LINUX软中断-softirq的描述中,提到过ksoftirqd,这篇文章就介绍ksoftirqd

ksoftirqd 是什么?

ksoftirqd 是个内核线程,在创建的时候是绑定cpu的,每一个core对应生成一个ksoftirqd 线程
比如当前系统有4个core

~# ps aux | grep ksoftirqd
root        3  0.0  0.0      0     0 ?        S    14:20   0:00 [ksoftirqd/0] //core 0
root        9  0.0  0.0      0     0 ?        S    14:20   0:00 [ksoftirqd/1] //core 1
root       12  0.0  0.0      0     0 ?        S    14:20   0:00 [ksoftirqd/2] //core 2
root       15  0.0  0.0      0     0 ?        S    14:20   0:00 [ksoftirqd/3] //core 3
ksoftirqd 的作用

ksoftirqd 的作用就是处理softirq用,它的本质就是调用 __do_softirq

ksoftirqd 的触发条件
  • 通过中断
    上一篇文章已经解释过了 softirq的处理是通过中断服务程序的第二个阶段,即中断来了执行ISR后,调用do_softirq遍历数组softirq_vec执行数组项的action(比如tasklet类型的softirq),但是有个问题,比如softirq_vec的第6个数组项(任务tasklet)执行完,恰好又来了个ISR提交tasklet,使得softirq_vec[6]又被需要处理,这可如何是好呢?
    总不能老是卡在中断流程里吧,这时就要唤醒本core上的ksoftirqd来处理softirq_vec数组,看下代码
static int run_ksoftirqd(void * __bind_cpu)
{set_current_state(TASK_INTERRUPTIBLE);while (!kthread_should_stop()) {preempt_disable();if (!local_softirq_pending()) {schedule_preempt_disabled();}__set_current_state(TASK_RUNNING);while (local_softirq_pending()) {if (local_softirq_pending())//do_softirq会再次通过本core的__softirq_pending 变量来遍历softirq_vec数组__do_softirq();}}return 0;
}
  • tasklet_schedule
    上篇文章讲过 tasklet是通过调用 tasklet_schedule来提交 ,tasklet_schedule除了可以再ISR中被调用之外,其实也可以在普通的线程中调用。
void __tasklet_schedule(struct tasklet_struct *t)
{unsigned long flags;local_irq_save(flags);t->next = NULL;*__this_cpu_read(tasklet_vec.tail) = t;__this_cpu_write(tasklet_vec.tail, &(t->next));raise_softirq_irqoff(TASKLET_SOFTIRQ);local_irq_restore(flags);
}
//raise_softirq_irqoff
inline void raise_softirq_irqoff(unsigned int nr)
{__raise_softirq_irqoff(nr);/** If we're in an interrupt or softirq, we're done* (this also catches softirq-disabled code). We will* actually run the softirq once we return from* the irq or softirq.** Otherwise we wake up ksoftirqd to make sure we* schedule the softirq soon.*/if (!in_interrupt())wakeup_softirqd();
}static void wakeup_softirqd(void)
{/* Interrupts are disabled: no need to stop preemption */struct task_struct *tsk = __this_cpu_read(ksoftirqd);if (tsk && tsk->state != TASK_RUNNING)wake_up_process(tsk);
}

由上面的代码得知,tasklet_schedule如果在中断上下文被调用的话(ISR中调用),则不唤醒ksoftirqd,会交给中断的下半段处理tasklet,如果在普通线程被调用的话,则唤醒ksoftirqd。注意,唤醒的是本core上的ksoftirqd

tasklet的缺点

tasklet感觉很少被用到,特点是执行权限比较高,执行时不能被打断,实时性比较好,缺点也明显,因为tasklet在执行过程中本core不能运行其他程序,如果tasklet运行时间长的话,会导致其他程序久久不被运行或者其他程序会抢别的core运行,导致系统性能下降。

实际项目中遇到的问题

要求display的显示是 60fps, insmod sd卡驱动后,显示会掉帧,导致降到 50fps左右
原因是sd卡驱动在检测sd卡是否插入用的是loop的方式检测gpio的值,而loop是利用mod_timer这种精度定时器来做的(300ms一次检测)
mod_timer实现机制是用tasklet来做的(TIMER_SOFTIRQ),即mod_timer所注册的回调函数会在softirq_vec[TIMER_SOFTIRQ]->action中被调用(run_timer_softirq)
每次tick产生后,就会判断一下timer时间到没到,如果到了,就会raise_softirq触发软中断来处理回调函数。

tick_handle_periodic()
->tick_periodic()->update_process_times()->run_local_timers()->hrtimer_run_queues()->__run_hrtimer(timer, &base->softirq_time);->raise_softirq(TIMER_SOFTIRQ)->run_timer_softirq->处理 mod_timer所注册timer的回调函数

而在本地问题中,timer的回调函数的处理中用到了如下的做法:

for (i=0; i<5; i++) {gpio_val += (smc_host->cd_mode == CARD_DETECT_BY_GPIO_IRQ_UP)\? (!__gpio_get_value(cd->gpio))\:( __gpio_get_value(cd->gpio));\mdelay(1);}

即循环5次检测gpio,并且还用mdelay来进行延迟,(此函数意味着本core不能被切走必须死等, 这也是没办法的,因为回调函数是在softirq上下文运行的,不能使用睡眠类的函数,这里mdelay不会睡眠)
这就造成了一次timer回调的执行至少需要5-6ms,因为系统有两个sd卡卡槽,一个卡槽对应一个timer进行检测,相当于执行一次需要10-12ms
而且softirq上下文导致其他线程不能被执行,从而大大影响系统性能。

tasklet处理中能否调用sleep函数

注意,在linux中,只要是中断上下文,是不允许调用schedule这种函数切走的,因为中断上下文即原子上下文,所以tasklet中不能用睡眠函数(本身你用了tasklet,就说明要处理的东西优先级高),但我偏偏要主动schedule切走呢?其实实验中发现也没什么问题,但是schedule会报错(scheduling while atomic)

static noinline void __schedule_bug(struct task_struct *prev)
{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);dump_stack();
}

因为上篇文章讲过,内核调度的原则是preempt_count为0,你偏偏非要preempt_count不为0的时候主动调用sleep等切走的话,内核__schedule函数会给你一个bug,但是为了尽可能的维持系统的运行,还坚持在跑,并且试图修正整个preempt_count (不在本次的讨论范围之内)

ISR处理中能否调用sleep函数

注意: 中断服务程序 = ISR + softirq
softirq执行的时候毕竟是开中断的,即便softirq执行中主动切走当前进程A,因为tick中断还是开的,还是会被切回来的。
我们模拟下流程:

  1. 进程A运行
  2. 来中断,进程A被打断,执行softirq(A)
  3. 在softirq中调用__schedule函数主动切走
  4. 切走时当前进程的thread info 变量栈中保存的上下文是:A进程的上下文 + softirqA)的上下文
  5. 切到B中运行
  6. 来了个tick中断,中断退出后,在切回进程A,先把softirqA)的上下文从thread info中出栈
  7. 继续执行 softirq(A), softirq(A)执行完后,把A进程的上下文从thread info中出栈
  8. 运行进程A

所以整个流程都没什么太大问题,但是在ISR中调用__schedule时问题就大了。
因为在执行ISR时,中断是关的,即本core上不会再有中断了,相当于把调度器也关了。
ISR中调用__schedule走动切走后,因为调度器也被关了,系统就挂掉了,当然如果SMP的场合下,因为其他core没有关中断,即tick中断还在继续,可能系统会在坚持跑(此时系统就比较奇怪了),会产生莫名其妙的bug。

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...