linux 软中断入门

在 linux 中,任务执行的载体有很多,包括线程,中断,软中断,tasklet,定时器等。但是从本质上来划分的话,任务执行的载体只有两个:线程和中断。软中断和 tasklet 的执行可能在中断中,也可能在线程中,定时器的执行可能在中断、软中断或者线程中。

在讨论中断和软中断的时候,经常以网卡收包为例子来理解。如下是网卡收包的主要环节,分别说明每个环节:

① 网卡从链路上收包,网卡的作用是成帧,网卡在链路上收到的是字节流,根据前导码,帧开始界定符,帧间隙来界定一个帧,这就是成帧。

② 网卡界定一帧报文之后,将这一帧报文通过 dma 保存到内存中。

③ 网卡触发中断,收包中断有对应的处理程序,对于现在的大多网卡来说,在中断处理程序中做的事情是触发收包软中断。

④ 收包的主要工作是在软中断中处理,而不是在硬中断中处理。

中断是硬件和软件进行通信的一种方式。中断具有异步,响应快的特点。在 linux 中,中断可以打断当前正在运行的程序,并且在处理一个中断的时候,往往需要关闭本地中断。

在网络收包的过程中,从网卡驱动,到链路层代码,再到 ip 层,tcp 层,报文的处理过程比较长,所需要花费的时间往往是比较长的。如果在中断处理程序中把收包的工作都做了,那么很可能会导致本地关中断时间太长,进而影响系统的响应。所以在网卡收包时,往往采用中断和软中断结合的方式,在中断处理程序中做的工作比较简单,只是触发一个软中断,后边的处理过程在软中断中进行。中断处理程序中触发软中断之后立即返回,此时,就可以打开本地中断了。

软中断处理函数是 __do_softirq(),从这个函数中也可以看出来,在处理软中断之前调用函数 local_irq_enable(),也就说在处理软中断的时候,是开中断的。

asmlinkage __visible void __softirq_entry __do_softirq(void)
{...local_irq_enable();h = softirq_vec;while ((softirq_bit = ffs(pending))) {unsigned int vec_nr;int prev_count;h += softirq_bit - 1;vec_nr = h - softirq_vec;prev_count = preempt_count();kstat_incr_softirqs_this_cpu(vec_nr);trace_softirq_entry(vec_nr);h->action(h);trace_softirq_exit(vec_nr);if (unlikely(prev_count != preempt_count())) {pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",vec_nr, softirq_to_name[vec_nr], h->action,prev_count, preempt_count());preempt_count_set(prev_count);}h++;pending >>= softirq_bit;}if (__this_cpu_read(ksoftirqd) == current)rcu_softirq_qs();local_irq_disable();...
}

1 软中断种类

如下是软中断的种类,当前定义了 10 种软中断,其中 NET_TX_SOFTIRQ 和 NET_RX_SOFTIRQ  用于网卡发包和收包。从定义可以看出来,软中断的种类是静态的,确定的,不能动态改变。从注释可以看出来,不到万不得已,不要新增软中断,大部分情况下使用 tasklet 已经足够了。

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ highfrequency threaded job scheduling. For almost all the purposestasklets are more than enough. F.e. all serial device BHs etal. should be converted to tasklets, not to softirqs.*/enum
{HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,IRQ_POLL_SOFTIRQ,TASKLET_SOFTIRQ,SCHED_SOFTIRQ,HRTIMER_SOFTIRQ,RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */NR_SOFTIRQS
};

 

2 开启软中断和触发软中断

以网卡软中断为例来说明。

通过函数 open_softirq() 来打开软中断,也叫注册这个软中断,在内核初始化的时候会调用函数 net_dev_init() 完成网络相关的初始化,在该函数中打开了网络收发包软中断。

static int __init net_dev_init(void)
{...open_softirq(NET_TX_SOFTIRQ, net_tx_action);open_softirq(NET_RX_SOFTIRQ, net_rx_action);...
}

软中断使用一个数组来维护,数组是 softirq_vec,数组的下标是软中断的中断号,数组的元素struct softirq_action 类型,这个结构体中只有一个成员,就是软中断的处理函数。网卡发包和收包软中断的处理函数分别是 net_tx_action 和 net_rx_action。

struct softirq_action
{void	(*action)(struct softirq_action *);
};static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

在网卡中断处理程序中,一般会调用函数 __raise_softirq_irqoff() 触发软中断。触发软中断就是在一个全局的变量中设置一个标志,标志有软中断了。在 linux 中,这个全局变量是 per cpu 的。

void __raise_softirq_irqoff(unsigned int nr)
{lockdep_assert_irqs_disabled();trace_softirq_raise(nr);or_softirq_pending(1UL << nr);
}

3 处理软中断

3.1 中断返回时

中断返回时,最终会调用到函数 __irq_exit_rcu(),在这个函数中会进行判断,如果当前不是处于中断上下文并且有软中断需要处理,那么就会调用 invoke_softirq() 来处理软中断。

static inline void __irq_exit_rcu(void)
{
#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLEDlocal_irq_disable();
#elselockdep_assert_irqs_disabled();
#endifaccount_irq_exit_time(current);preempt_count_sub(HARDIRQ_OFFSET);if (!in_interrupt() && local_softirq_pending())invoke_softirq();tick_irq_exit();
}

在函数 invoke_softirq() 中进行判断,如果软中断处理线程当前正在运行,那么直接返回。如果 force_irqthreads 是 false 的话,那么就调用 __do_softirq() 或者 do_softirq_onwn_stack() 处理软中断;如果强制让软中断处理线程来处理软中断,那么意思是在中断返回的时候不处理软中断,会将线程唤醒。

static inline void invoke_softirq(void)
{if (ksoftirqd_running(local_softirq_pending()))return; if (!force_irqthreads) {
#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK/** We can safely execute softirq on the current stack if* it is the irq stack, because it should be near empty* at this stage.*/__do_softirq();
#else/** Otherwise, irq_exit() is called on the task stack that can* be potentially deep already. So call softirq in its own stack* to prevent from any overrun.*/do_softirq_own_stack();
#endif} else {wakeup_softirqd();}
}

3.2 ksoftirqd

除了在中断返回的时候处理软中断,还有专门的线程来处理。在内核中,每个 cpu 核上都会创建一个 ksoftirqd 线程用来处理这个核上的软中断。

ksoftirqd 的信息维护在 softirq_threads 中。

static struct smp_hotplug_thread softirq_threads = {.store            = &ksoftirqd,.thread_should_run    = ksoftirqd_should_run,.thread_fn        = run_ksoftirqd,.thread_comm        = "ksoftirqd/%u",
};

 

在 ksoftirqd 中,最终也是调用函数 __do_softirq() 来处理软中断。

static void run_ksoftirqd(unsigned int cpu)
{local_irq_disable();if (local_softirq_pending()) {/** We can safely run softirq on inline stack, as we are not deep* in the task stack here.*/__do_softirq();local_irq_enable();cond_resched();return;}local_irq_enable();
}

什么时候在中断返回时处理软中断 ?

中断返回时会进行判断,如果当前不是处于中断上下文,并且有软中断需要处理,并且这个时候 ksofrirqd 没有在运行,那么这个时候就会处理软中断。

什么时候在 ksoftirqd 中处理软中断 ?

① 中断返回的时候, 判断 force_irqthreads 为 true,那么只能在 ksoftirqd 中处理软中断,这个时候会通过 wakeup_softirqd() 来唤醒 ksoftirqd。

② 使用 raise_softirq_irqoff() 触发软中断时,如果当前不是处于中断上下文,也会唤醒 ksoftirqd

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();
}

4 软中断处理时为什么不能睡眠

软中断的处理,可能在中断返回的时候,也可能在 ksoftirq 线程中。在中断返回的时候,虽然通过 in_interrupt() 判断,当前不是处于中断上下文,但是这个时候中断处理程序还没有真正返回,还不是进程上下文,所以此时也是不能睡眠。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784172.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DevSecOps安全工具链介绍

目录 一、概述 二、安全工具链在平台中的定位 2.1 概述 2.2 分层定位 2.2.1 不同阶段的安全工具 2.2.2 安全工具金字塔 2.3 安全流水线集成概览 2.3.1 概述 2.3.2 标准流水线集成安全工具链概览图 三、安全工具链分类 3.1 概述 3.2 威胁建模类 3.2.1 威胁建模的概念…

Vue企业级项目开发axios

axios二次封装 import axios from axios 导入element-plus的信息弹窗 imort {elMessage} from element-plus//利用axios的create方法创建实例&#xff0c;配置默认请求头和请求超时时间 const request axios.create({url:/api,可以使用已经配置好的环境变量import.meta.env.V…

计算机网络:数据链路层 - 封装成帧 透明传输 差错检测

计算机网络&#xff1a;数据链路层 - 封装成帧 & 透明传输 & 差错检测 数据链路层概述封装成帧透明传输差错检测 数据链路层概述 从数据链路层来看&#xff0c;主机 H1 到 H2 的通信可以看成是在四段不同的链路上的通信组成的&#xff0c;所谓链路就是从一个节点到相邻…

Android设备无线连接电脑及QXDM、QACT等工具的方法

首先样机和笔记本电脑连接同一wifi网络 adb root adb shell ifconfig复制inet addr地址 ping inet addr地址 adb tcpip 5555 adb connect (inet addr地址):5555 此时adb和机器使用wifi连接好了&#xff0c;可以拔出usb线 ipconfig查询电脑的IP地址 ipconfig使用adb在主机上…

2024系统架构师---解释器架构风格的概念与应用

解释器架构风格是一种软件架构模式&#xff0c;用于构建那些能够读取、解析并执行用户定义的命令或程序代码的系统。这种架构风格的关键在于提供一个运行时环境&#xff0c;它能够理解和执行预定义或用户定义的语言或指令集。通过这种方式&#xff0c;解释器模式能够为特定领域…

设计模式学习笔记 - 设计模式与范式 -行为型:1.观察者模式(上)

概述 前面已经学习了创建型和结构性设计模式&#xff0c;从本章开始开始学习行为型设计模式。创建型设计模式主要解决 “对象的创建” 问题&#xff0c;结构性设计模式主要解决 “类或对象的组合或组装” 问题&#xff0c;行为型设计模式主要解决 “类或对象之间的交互” 问题…

canvas+javascript 实现贪吃蛇游戏

引言 在当今数字化时代&#xff0c;编程已经成为一种极具创造力和趣味性的活动。通过编写代码&#xff0c;我们可以创造出各种各样的应用程序和游戏&#xff0c;其中包括经典的贪吃蛇游戏。本文将向您介绍如何使用 JavaScript 编程语言制作一个简单而有趣的贪吃蛇游戏&#xf…

Kafka(十一)管理Kafka

目录 管理Kafka1 命令行操作1.1 Topic操作1.1.1 创建主题1.1.2 列出集群所有主题1.1.3 列出主题详情1.1.4 增加主题分区数1.1.5 减少主题分区数1.1.6 删除主题 1.2 生产和消费1.2.1 控制台生产者1.2.2 控制台消费者 1.3 消费者群组1.3.1 列出并描述群组1.3.2 删除消费者群组1.3…

动态内存管理-错题合集讲解

空指针的解应用操作&#xff08;错误信息合集&#xff09; 越界访问 首先我们上一个代码&#xff0c;看看这个的代码的问题 这个代码的问题显而易见 &#xff0c;就是在循环里面&#xff0c;产生了越界访问的问题&#xff0c;这里你开辟了10个整形空间&#xff0c;但是从0-1…

【javaWeb 第九篇】功能接口开发流程以及常用注解

常用注解 准备-环境搭建开发规范开发流程 注解补充 准备-环境搭建 准备数据库表&#xff08;dept,emp&#xff09;准备后端SpringBoot环境 需要依赖&#xff1a; Web起步依赖&#xff0c;数据库驱动依赖&#xff0c;Mybatis依赖&#xff0c;lombok依赖配置文件application.pr…

9.Python类与对象

1 面向对象 类和对象都是面向对象中的重要概念。面向对象是一种编程思想&#xff0c; 即按照真实世界的思维方式构建软件系统。 例如&#xff0c;在真实世界的校园里有学生和老师&#xff0c;学生有学号、姓名、所 在班级等属性&#xff08;数据&#xff09;&#xff0c;还有…

标题:AI大模型学习:解放智能的未来之路

随着技术的不断进步和发展&#xff0c;AI大模型学习成为了当前人工智能领域的热点话题。AI大模型学习不仅仅需要研究者具备深厚的数学基础和编程能力&#xff0c;还需要对特定领域的业务场景有深入的了解。通过不断优化模型结构和算法&#xff0c;AI大模型学习能够不断提升模型…

MySQL核心命令详解与实战,一文掌握MySQL使用

文章目录 文章简介演示库表创建数据库表选择数据库删除数据库创建表删除表向表中插入数据更新数据删除数据查询数据WHERE 操作符聚合函数LIKE 子句分组 GROUP BY HAVINGORDER BY(排序) 语句LIMIT 操作符 分页查询多表查询-联合查询 UNION 操作符多表查询-连接的使用-JOIN语句编…

本地GPU调用失败问题解决3重新配置anaconda环境(成功)

1、右键“以管理员身份”打开anaconda prompt conda create -n python 3.9 2、使用官方下载源的配置 3、修改conda下载超时 conda config --set remote_connect_timeout_secs 60 conda config --set remote_read_timeout_secs 100 查看配置结果conda config --show 配置内…

122、内网安全——域信息收集应用网络凭据CS插件AdfindBloodHound

文章目录 理解域域信息搜集 理解域 假设有1000台计算机&#xff0c;运维人员需要为每一台计算机进行软件的安装、环境部署&#xff0c;实际上运维人员不可能亲自对每一台计算机进行软件的安装和环境部署。实际&#xff0c;将所有1000台计算机放入一个域中&#xff0c;域内有一…

多传感器标定——相机内参标定

文章目录 一、前言二、内参标定流程三、如何提升标定精度四、精度验证五、内外参联合标定 一、前言 之前写过一篇文章&#xff08;相机内参、外参、畸变系数简介&#xff09;&#xff0c;感觉应该把这几个东西说的还算明白&#xff0c;但是里边并没有深究该如何进行标定&#…

【软考---系统架构设计师】物联网和云计算

目录 一、物联网 二、云计算 一、物联网 物联网是实现物物相连的互联网络&#xff0c;其内涵包括两个方面&#xff1a;第一&#xff0c;物联网的核心和基础仍是互联网&#xff0c;是在互联网基础上延伸和扩展的网络&#xff1b;第二&#xff0c;其用户端延伸和扩展到了任何物…

牛客NC153 信封嵌套问题【中等 动态规划,最长递增子序列 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/9bf77b5b018d4d24951c9a7edb40408f 相同的题目&#xff1a; https://www.lintcode.com/problem/602 思路 本质是求最长子序列问题envelopes 先按 w 升序排序&#xff0c;再按 h 降序 排序&#xff0c;只需考虑h…

一条SQL在MySQL中的执行过程

图解&#xff1a; 第⼀步&#xff1a;连接器 过程 1. 建⽴连接&#xff1a;与客户端进⾏ TCP 三次握⼿建⽴连接&#xff1b; 2. 校验密码&#xff1a;校验客户端的⽤户名和密码&#xff0c;如果⽤户名或密码不对&#xff0c;则会报错&#xff1b;3. 权限判断&#xff1a…

手机无线投屏到windows11电脑

1 安装无线投影组件 2 电脑端打开允许其他设备投影的开关 3 手机找到投屏选项 4 手机搜索可用设备连接即可 这里的官方文档给的不太好,给了一些让人眼花撩乱的信息,以下是经过整合的有效信息