LINUX软中断-ksoftirqd

2023-11-11

前言

在上一篇 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上不会再有中断了,相当于调度器也关了,所以主动且走后,就没办法在切回来了,其实这种理解是错的!哈哈哈!
答案是,__schedule要切的时候,会把本core中断再次打开

raw_local_irq_disable() //关闭本地中断
schedule() //调用schedule()函数来切换进程
raw_local_irq_enable() //打开本地中断

假设进程A在关闭本地中断的情况下切换到进程B来运行,进程B切换执行时会打开本地中断,以防止系统瘫痪。我们看下代码:

__schedule (A) //A在cpu0上
	context_switch
		switch_to (B) //多核的情况下,Bthread可能在thread1上被唤醒
			finish_task_switch
				finish_lock_switch(rq, prev);
					raw_spin_rq_unlock_irq
						local_irq_enable //开中断

但是有个问题,单核的情况下没什么问题,B跟A都是同一个cpu,B被唤醒时,把中断打开,但是多核呢?其实那也没关系,等B在切换回A时,或者说,有一个thread从cpu0唤醒时,都会先打开中断!
也就是说,在中断中是可以切走的。经过实测,在ISR中与softirq中都可以调用schedule切走,但是会报BUG(因为在中断上下文中调用schedule,schedule设计原则是不允许切换的,但是你非要这么做,它只能就范,但是又为什么不让系统瘫痪,故报一个bug提醒你)。

BUG: scheduling while atomic: swapper/0/0/0x00010002

但是不管怎么说,设计初衷就是不允许被抢占,而且在中断走切走基本上就是bug,比如你在A thread用了spinlock,然后来了个中断,中断主动schedule后切到B thread了,B thread要spinlock,就死锁了(A在中断时被切走)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LINUX软中断-ksoftirqd 的相关文章

  • 如何使用libaudit?

    我试图了解如何使用 libaudit 我想接收有关使用 C C 的用户操作的事件 我不明白如何设置规则 以及如何获取有关用户操作的信息 例如 我想获取用户创建目录时的信息 int audit fd audit open struct aud
  • 使用 MongoDB docker 镜像停止虚拟机而不丢失数据

    我已经在 AWS EC2 上的虚拟机中安装了官方的 MongoDB docker 映像 并且数据库上已经有数据 如果我停止虚拟机 以节省过夜费用 我会丢失数据库中包含的所有数据吗 在这些情况下我怎样才能让它持久 有多种选择可以实现此目的 但
  • Linux TCP服务器:在接受连接之前读取客户端的IP地址

    Related C Winsock API如何在接受连接之前获取连接客户端IP https stackoverflow com questions 716209 c winsock api how to get connecting cli
  • 使用netcat将unix套接字传输到tcp套接字

    我正在尝试使用以下命令将 unix 套接字公开为 tcp 套接字 nc lkv 44444 nc Uv var run docker sock 当我尝试访问时localhost 44444 containers json从浏览器中 它不会加
  • 原生 Linux 应用程序可像 ResHacker 一样编辑 Win32 PE

    我想运行自动修改 dll服务 用户提交特定的 dll 我在服务器上修改它 然后用户可以下载 dll的修改版本 是否有任何本机 Linux 应用程序提供常见的 Win32 PE 修改功能 例如图标 字符串 加速器 对话等 至少提供命令行或脚本
  • Docker DNS 设置

    我尝试使用自定义网络和 dos 设置创建 docker 容器 docker网络创建 driver bridge opt com docker network bridge enable ip masquerade true opt com
  • 如何在两个不同帐户之间设置无密码身份验证

    我们可以在两台机器的两种不同用途之间设置无密码身份验证吗 例如 计算机A有用户A 计算机B有用户B 我们可以设置密码 ssh 以便计算机 A 上的用户 A 使用其用户帐户 A 登录计算机 B 谢谢你 如果我理解你的问题 你能设置一下吗ssh
  • 如何仅将整个嵌套目录中的头文件复制到另一个目录,在复制到新文件夹后保持相同的层次结构

    我有一个目录 其中有很多头文件 h 和其他 o 和 c 文件以及其他文件 这个目录里面有很多嵌套的目录 我只想将头文件复制到一个单独的目录 并在新目录中保留相同的结构 cp rf oldDirectory newDirectory将复制所有
  • Windows 与 Linux 文本文件读取

    问题是 我最近从 Windows 切换到 Ubuntu 我的一些用于分析数据文件的 python 脚本给了我错误 我不确定如何正确解决 我当前仪器的数据文件输出如下 Header 有关仪器等的各种信息 Data 状态 代码 温度 字段等 0
  • 使用命令行将 MediaWiki 维基文本格式转换为 HTML

    我倾向于编写大量文档 因此 MediaWiki 格式对我来说很容易理解 而且比编写传统 HTML 节省了我很多时间 然而 我也写了一篇博客 发现一直从键盘切换到鼠标来输入正确的 HTML 标签会增加很多时间 我希望能够使用 Mediawik
  • Linux 中的电源管理通知

    在基于 Linux 的系统中 我们可以使用哪些方法 最简单的方法 来获取电源状态更改的通知 例如 当计算机进入睡眠 休眠状态等时 我需要这个主要是为了在睡眠前保留某些状态 当然 在计算机唤醒后恢复该状态 您只需配置即可获得所有这些事件acp
  • Bash 脚本 - 迭代 find 的输出

    我有一个 bash 脚本 其中需要迭代 find 命令输出的每一行 但似乎我正在迭代 find 命令中的每个单词 以空格分隔 到目前为止我的脚本看起来像这样 folders find maxdepth 1 type d for i in f
  • 我应该使用哪个 Linux 发行版作为 Xen 主机? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我为家庭办公室订购了一台服务器 我想用 Xen 对其进行分区 我认为这将使事情保持干净并且更容易维护 我将运行 MySQL PostgreSQL
  • “grep -q”的意义是什么

    我正在阅读 grep 手册页 并遇到了 q 选项 它告诉 grep 不向标准输出写入任何内容 如果发现任何匹配 即使检测到错误 也立即以零状态退出 我不明白为什么这可能是理想或有用的行为 在一个程序中 其原因似乎是从标准输入读取 处理 写入
  • Linux 上的 Python 3.6 tkinter 窗口图标错误

    我正在从 Python GUI 编程手册 学习 Python GUI 某项任务要求我通过将以下代码添加到我的配方中来更改窗口图标 Change the main windows icon win iconbitmap r C Python3
  • 将数组传递给函数名称冲突

    Specs GNU bash 版本 3 1 17 无法升级 Premise 我一直在摆弄数组 我想知道是否有任何方法可以让函数的本地变量与所述函数外部的数组同名 Example 在下面的示例中 我将尝试显示该问题 Working bin b
  • gentoo crontab:为什么这个简单的 crontab 不起作用?

    我使用 GENTOO 发行版 crontab e 35 12 root php5 home www cron php 当我手动运行时 php5 php5 home www cron php 这有效 它向我发送了一封电子邮件 然后我检查日期
  • 停止服务时单元陷入故障状态(状态=143)[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 这是我的问题 我有 CentOS 和 java 进程在上面运行 Java进程是通过启动 停止脚本来操作的 它也创建了 java 实例的 p
  • 在 LINUX 上使用 Python 连接到 OLAP 多维数据集

    我知道如何在 Windows 上使用 Python 连接到 MS OLAP 多维数据集 嗯 至少有一种方法 通常我使用 win32py 包并调用 COM 对象进行连接 import win32com client connection wi
  • 无法安装 WWW::Curl::Easy: SZBALINT/WWW-Curl-4.17.tar.gz : make NO

    我正在尝试在我的 Fedora 26 机器上安装 WWW Curl Easy gcc c I usr include D REENTRANT D GNU SOURCE O2 g pipe Wall Werror format securit

随机推荐

  • python爬取网页的方法总结,python3.9爬取网页教程

    大家好 小编为大家解答python 爬取网页内容并保存到数据库的问题 很多人还不知道利用python爬取简单网页数据步骤 现在让我们一起来看看吧 需求分析 今天遇到一个简单的需求 需要下载澳大利亚电力市场NEM日前市场的发电商报价数据 ne
  • LeetCode题目笔记——965. 单值二叉树

    文章目录 题目描述 题目链接 题目难度 简单 方法一 遍历 哈希表 代码 Python 方法二 深度优先 代码 Python 总结 题目描述 如果二叉树每个节点都具有相同的值 那么该二叉树就是单值二叉树 只有给定的树是单值二叉树时 才返回
  • Field 'id' doesn't have a default value问题解决方法

    Field id doesn t have a default value问题解决方法 突然想温习温习对数据库的读写 于是就用mysql建了一张单独的表 见代码1 用Hibernate写了个应用 可以正常查询 修改数据了 开始时 数据是在m
  • IDEA——Java:程序包xxxx不存在终极方案总结

    最近在接手一个新的java项目 导入到IDEA后发现存在报错 程序包找不到 寻思应该是某些依赖没有加载进来 但几番尝试后发现问题依旧 于是决定调研下对应的解决方案 说实话类似这种问题的解决方案网上一搜一大堆 但试了很多根本不管用 其实大多数
  • Vue项目,通过数组下标更改数组的值不生效,页面没有重新渲染

    大家好 我是小梅 公众号 小梅的前端之路 原创作者 作为在前端领域不断探索的一员 在此记录开发中遇到的问题 如果你也遇到了相同的问题 希望本文对你有帮助 问题背景 今天在开发中遇到了一个需要 在列表里要通过按钮的点击控制手机号码列是显示正常
  • vue实现连接Mysql数据库和服务器通信

    在vue中 我们不仅是为了做前端页面展示和数据响应式 最重要的还是去访问后端请求 把数据给数据库存储 这样刷新页面数据才不会丢失 vue中对于数据的发送以及存储也很重要 稍不注意也会存在很大的问题 所以我们要学习一下存储数据以及对数据的请求
  • IDEA运行jar包不存在问题

    写在前面 本博客中解决方法适用情景为依赖包导入无错的情况下 即IDEA原因 解决方案 点击File gt Invaildate Cache Restart重启IDEA 2 若1无效 cmd路径切换到项目下执行mvn idea idea 再打
  • java 运行报错has been compiled by a more recent version of the Java Runtime (class file version 54.0)

    报错信息 Exception in thread main java lang UnsupportedClassVersionError pers cyz BookManage has been compiled by a more rec
  • p值小于0.05拒绝还是接受_25常见种误区:P值、置信区间和统计功效

    连享会主页 lianxh cn 连享会 小直播 Stata 数据清洗 第二季 连享会 计量专题 因果推断 内生性 专题 2020 11 12 15 主讲 王存同 中央财经大学 司继春 上海对外经贸大学 课程主页 https gitee co
  • Shell笔记--使用系统函数、自定义函数和Shell工具

    目录 1 basename和dirname系统函数 2 自定义函数 3 Shell常用工具 3 1 cut 3 2 sort 1 basename和dirname系统函数 basename 基本用法 basename string path
  • LiveData的使用及详解

    1 LiveData简单使用 本篇文章代码实现部分主要使用Java进行讲解 LiveData主要方便用于数据的观察 进行UI更新或者业务处理等操作 如下为LiveData的简单代码实现 创建一个MutableLiveData对象 这个使用L
  • ModuleNotFoundError: No module named ‘pygame’——Python3.6安装pip并下载pygame模块

    问题 今天学习python的时候 运行时报错 ModuleNotFoundError No module named pygame 意思就是没有 pygame 这个模块 解决办法 下载一下这个模块就行了 pip3 install pygam
  • 给你一个电商网站,你如何测试?

    当下软件测试主流方向是Web端和移动端应用 但无论是哪个端 多数都可以基于软件测试的六个方向来测试 即功能 性能 易用性 可靠性 兼容性 有效性这几个方面考虑 如果给你一个电商网站 你该如何测试 以下是测试重点 文末有福利 一 功能测试 链
  • 光学基础知识:焦点、弥散圆、景深、焦深

    1 焦点 focus 与光轴平行的光线射入凸透镜时 理想的镜头应该是所有的光线聚集在一点后 再以锥状的扩散开 来 这个聚集所有光线的一点 就叫做焦点 2 弥散圆 circle of confusion 在焦点前后 光线开始聚集和扩散 点的影
  • 原生js 传数组作为参数 之get /post

    1 get 方式 var getData questionIDs qustionArr var questionArr questionArr push 12345678 定义一个函数 关键就在这里 原生的请求传数组是行不通的 需要将数据做
  • Excalidraw本地化部署

    Excalidraw本地化部署 官方地址 https excalidraw com 为什么要本地话部署呢 因为官方不支持中文手写体 Excalidraw介绍 Excalidraw是一个开源 小巧易用的手写风格的在线绘图工具 本地部署 在te
  • mysql死锁分析工具show engine innodb status

    参考文章 记录一次MySQL死锁的分析与解决过程 mysql之show engine innodb status解读 把MySQL中的各种锁及其原理都画出来 写在开头 本文为学习后的总结 可能有不到位的地方 错误的地方 欢迎各位指正 前言
  • 单片机c语言如何表示二进制,单片机C语言中将二进制数转化为十进制的办法

    单片机C语言中将二进制数转化为十进制的办法 1 最简单最直观的方法 将2进制方式表示的数转化为10进制表示的数 要用除10取余法 步骤如下 被除数记为x 10进制表示的结果用数组a表示 1 i 0 2 a i x 10 x x 10 i 3
  • 图灵学院Java架构师课程,基于java

    主要功能模块 1 登录 输入账号密码和验证码登录 2 用户信息模块 3 菜单模块 4 角色模块 5 项目竞赛活动申请模块 6 项目竞赛经费申请模块 7 项目竞赛活动管理审批模块 8 项目个人赛报名模块 9 项目团队赛报名模块 10 项目结题
  • LINUX软中断-ksoftirqd

    前言 在上一篇 LINUX软中断 softirq的描述中 提到过ksoftirqd 这篇文章就介绍ksoftirqd ksoftirqd 是什么 ksoftirqd 是个内核线程 在创建的时候是绑定cpu的 每一个core对应生成一个kso