Linux 中断处理
一、基本概念
1、中断及中断上下文
中断是一种由硬件设备产生的信号,不同设备产生的中断通过中断号来区分。CPU在接收到中断信号后,根据中断号执行对应的中断处理程序(Interrupt Service Routine)
内核对异常和中断的处理类似,差别只在于中断是由硬件引起的
异常举例:软中断实现系统调用,缺页异常,除0异常
中断上下文:执行一个中断处理程序时,内核处于中断上下文中
注:在中断上下文中不允许睡眠,这是因为中断上下文没有后备进程,即无法被调度唤醒
2、上半部与下半部
一般将中断处理分为上半部和下半部
上半部:在接收到中断后只完成有严格时限要求的工作,如对中断应答或者复位硬件,此时中断是被禁止的
下半部:指中断处理流程中推后执行的那一部分,在合适的时机执行,此时允许相应所有的中断
3、下半部和推后执行的工作
除了对时间敏感且保证不被其他中断打断的部分,剩余的任务考虑在下半部执行,下半部的实现机制有以下几种
| 下半部机制 | 功能 | 状态 |
| BH(bottom half) | 一个静态创建、由32个bottom halves组成的链表,上半部通过32整数中的一位来标识出哪个bottom half可以执行,虽然分属不同处理器,也不允许任何两个bottom half同时执行。(不够灵活,简单有性能瓶颈) | Linux 2.5移除 |
| 任务队列 | 引入任务队列机制来实现工作的推后执行,替代BH机制。驱动程序会将下半部注册到相应的等待队列,等待调用执行。(不够灵活,不能满足性能要求较高的子系统) | Linux 2.5移除 |
| 软中断(Softirq) | 一组静态定义的下半部接口,有32个,可以在所有处理器上同时运行,同类型的接口也可以同时执行。tasklet是需要在编译阶段进行静态注册。(针对性能要求较高的子系统) | Linux 2.3引入 |
| tasklet | 一组基于软中断实现的灵活性强、动态创建的下半部实现机制, 不同类型的tasklet可以在不同的处理器上执行, 但类型相同tasklet,不能同时执行。tasklet可以通过代码进行动态注册。(大部分的场景) | Linux 2.3引入 |
| 工作队列(Work queues) | 工作队列取代了任务队列 | Linux 2.3引入 |
二、中断处理程序
1、注册中断处理程序
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name,void *dev);
irq:要分配的中断号
handler:指向这个中断的处理程序
flag:可以为0,也可以是一个或多个标志的掩码IRQF_DISABLED --- 内核在处理中断处理程序期间,禁止所有的其他中断IRQF_SAMPLE_RANDOM --- 该设备产生的中断对内核熵池有贡献,内核熵池负责从各个随机事件导出真正的随机数IRQF_TIMER --- 为系统定时器的中断处理而准备IRQF_SHARED --- 多个中断处理程序之间共享中断线,同一个中断线的所有的中断处理程序都要指定该标志
name:设备ASCLL文本,会被proc/interrupts文件使用,以便与用户通信
dev:主要用于共享中断线,dev提供唯一标志信息(cookie),用来区分共享一个中断的多个设备,当一个中断处理程序需要释放时,以便从共享中断线的诸多处理程序中删除指定的一个,如果没有共享中断,设置为NULL即可
注:此函数会睡眠,所以不要在中断上下文中调用
2、释放中断处理程序
void free_irq(unsigned int irq, void *dev)
卸载驱动处理程序时,需要注销相应的中断处理程序,并释放中断线
如果指定的中断线不是共享的,那么,函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的,则删除dev所对应的处理程序,共享中断线只有在删除了最后一个中断处理程序时才会被禁用。
3、中断处理主程序
static irqreturn_t intr_handler(int irq, void *dev);
irq:处理程序要响应中断的中断号
dev:与传递给request_irq()的dev保持一致,dev将提供唯一的标志信息(cookie),用来区分共享同一个中断处理程序的多个设备
Linux中断处理程序是不允许嵌套其他中断处理程序,中断上下文的代码应当迅速简洁,中断处理程序没有独立的栈,与所中断进程共享内核栈
4、中断控制
| 函数 | 说明 |
| local_irq_disable() | 禁止本地中断传递 |
| local_irq_enable() | 激活本地中断传递 |
| local_irq_save(unsigned long flags) | 保存本地中断传递的当前状态,然后禁止本地中断传递 |
| local_irq_restore(unsigned long flags) | 恢复本地中断传递到给定的状态 |
| disable_irq(unsigned int irq) | 禁止给定中断线,并确保该函数返回之前在该中断线上没有处理程序在运行 |
| disable_irq_nosync(unsigned int irq) | 禁止给定中断线,不会等待当前中断处理程序执行完毕 |
| enable_irq(unsigned int irq) | 激活给定中断线 |
| synchronize_irq(unsigned int irq) | 等待一个特定的中断处理程序退出,才会返回 |
| irqs_disabled() | 如果本地处理器上的中断系统被禁止,则返回非0;否则返回0 |
| in_interrupt() | 如果在中断上下文中(包括执行中断处理程序和正在执行下半部处理程序), 则返回非0, 如果在进程上下文中,则返回0 |
| in_irq() | 如果当前正在执行中断处理程序, 则返回非0;否则返回0 |
三、中断绑定
1、查看中断号
cat proc/interruptsCPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 11: 17280 12860 7073 8441 7254 10057 11085 10031 GICv3 27 Level arch_timer13: 0 0 0 0 0 0 0 0 GICv3 38 Level arch_mem_timer15: 0 0 0 0 0 0 0 0 GICv3 23 Level arm-pmu16: 0 0 0 0 0 0 0 0 GICv3 960 Edge gh_msgq_tx17: 9 0 0 0 0 0 0 0 GICv3 961 Edge gh_msgq_rx18: 13799 1329 0 0 0 0 0 0 GICv3 261 Level ipcc_019: 62 0 0 0 0 0 0 0 GICv3 94 Level qcom_cpucp1、irq逻辑中断号
2、中断在各CPU发生的次数
3、中断所属设备类名称
4、硬件中断号
5、中断触发方式
6、中断处理函数
2、将中断绑定到CPU
smp_affinity:通过 bitmask 算法绑定CPU echo 0xf > /proc/irq/45/smp_affinitysmp_affinity_list:通过数字指定CPU编号 echo 0-3 > /proc/irq/45/smp_affinity_list //作用是将中断号45的设备中断处理(包括软中断和硬中断)均摊绑定到0、1、2、3各个CPU
四、下半部机制
1、软中断
软中断是在编译期间静态分配的,软中断由softirq_action结构表示。软中断保留给系统中对时间要求最严格以及最重要的下半部使用
struct softirq_action
{void (*action)(struct softirq_action *);
};
注册的软中断,都在这个数组里面,每个被注册的软中断都占据该数组的一项
static struct softirq_action softirq_vec[NR_SOFTIRQS]
(1)软中断处理程序
同一个处理器,一个软中断不会抢占另外一个软中断,但中断处理程序可以抢占软中断。其他软中断(包括相同类型的软中断)可以在其他处理器上同时执行
void softirq_handler(struct softirq_action *);
(2)软中断执行程序
每个处理器都有一个ksoftirqd/n线程,他们会通过softirq_pending()发现是否有待处理的软中断,如果发现,就会调用do_softirq() 循环遍历,调用相应的处理程序
(3)软中断类型
下面是按照优先级排列的
/* 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, /*优先级较高的tasklet*/TIMER_SOFTIRQ, /*定时器的下半部*/NET_TX_SOFTIRQ, /*发送网络数据包*/NET_RX_SOFTIRQ, /*接收网络数据包*/BLOCK_SOFTIRQ, /*block装置*/IRQ_POLL_SOFTIRQ, TASKLET_SOFTIRQ, /*正常优先权的tasklet*/SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the numbering. Sigh! */RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */NR_SOFTIRQS
};
(4)使用软中断
a.注册软中断
open_softirq(NET_TX_SOFTIRQ,net_tx_action);
open_softirq(NET_RX_SOFTIRQ,net_rx_action);b.触发软中断
//将软中断设置成挂起状态,下次调用do_softirq()函数时投入运行。
raise_softirq(NET_TX_SOFTIRQ);
//中断已经被禁止,调用下面指令
raise_softirq_irqoff(NET_TX_SOFIRQ);
2、tasklet
tasklet是一种利用软中断实现的一种下半部机制, 但是,它的接口更简单,锁保护也要求较低
struct tasklet_struct
{struct tasklet_struct *next;//链表,指向下一个taskletunsigned long state;//tasklet状态, 0/准备执行/正在运行atomic_t count;//引用计数,为0才被激活void (*func)(unsigned long);//tasklet处理函数unsigned long data;//给tasklet处理函数的参数
};
(1)调度tasklet
已经通过tasklet_schedule()完成调度,等待执行的tasklet会被存放在两个链表tasklet_vec和tasklet_hi_vec,将软中断TASKLET_SOFTIRQ或HI_SOFTIRQ设置成挂起状态,等待下一次调用do_softirq()就会执行
tasklet_vec:存放普通的tasklet,由tasklet_schedule()进行调度,使用的是TASKLET_SOFTIRQ软中断。
tasklet_hi_vec:存放高优先级的tasklet,由tasklet_hi_schedule()进行调度,使用的是HI_SOFTIRQ软中断
//将软中断TASKLET_SOFTIRQ设置成挂起状态,等待do_softirq()调用raise_softirq_irqoff(softirq_nr);
(2)使用tasklet
静态创建一个tasklet结构
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
通过传入一个利用tasklet_init()函数动态创建tasklet结构
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
{t->next = NULL;t->state = 0;atomic_set(&t->count, 0);t->func = func;t->data = data;
}
EXPORT_SYMBOL(tasklet_init);
调度你创建的tasklet
tasklet_schedule(&my_tasklet)
3、工作队列
在中断处理中,经常用到工作队列,这样便能缩短中断处理时的时间。工作队列可以睡眠,比如需要获取大量内存或者需要获取信号量
Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程,方便了用户的编程
(1)常用函数
a.INIT_WORK()
通过此函数将work_struct与函数建立联系
#ifdef CONFIG_LOCKDEP
#define __INIT_WORK_KEY(_work, _func, _onstack, _key) \do { \__init_work((_work), _onstack); \(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \lockdep_init_map(&(_work)->lockdep_map, "(work_completion)"#_work, (_key), 0); \INIT_LIST_HEAD(&(_work)->entry); \(_work)->func = (_func); \} while (0)
#else
#define __INIT_WORK_KEY(_work, _func, _onstack, _key) \do { \__init_work((_work), _onstack); \(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \INIT_LIST_HEAD(&(_work)->entry); \(_work)->func = (_func); \} while (0)
#endif#define __INIT_WORK(_work, _func, _onstack) \do { \static __maybe_unused struct lock_class_key __key; \\__INIT_WORK_KEY(_work, _func, _onstack, &__key); \} while (0)#define INIT_WORK(_work, _func) \__INIT_WORK((_work), (_func), 0)work:work_struct结构体
func:函数名
struct work_struct {atomic_long_t data; /*工作处理函数func的参数*/ struct list_head entry; /*连接工作的指针*/ work_func_t func; /*函数指针,指向func函数*/
#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endif
};
b.schedule_work(work)
static inline bool schedule_work(struct work_struct *work)
{return queue_work(system_wq, work);
}
当中断来了,立马调用schedule_work(work),然后退出,此函数一般在中断上半部。
中断结束后,内核线程会自动调用work结构体对应的func函数
static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
{return queue_delayed_work(system_wq, dwork, delay);
}
有时并不希望work立即执行,而是希望过一段delay时间后再执行。工作队列是没有优先级的,基本按照FIFO的方式进行处理。
c.cancel_work_sync(work);
/*取消work结构体对应的func函数,但会等待其执行完,一般在exit中使用*/
bool cancel_work_sync(struct work_struct *work)
{return __cancel_work_timer(work, false);
}
EXPORT_SYMBOL_GPL(cancel_work_sync);
d.queue_work(workqueue, work)
/*调度执行一个指定workqueue中的任务*/
static inline bool queue_work(struct workqueue_struct *wq,struct work_struct *work)
{return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
e.flush_work(work)
/*等待工作队列完成执行工作*/
bool flush_work(struct work_struct *work)
{return __flush_work(work, false);
}
EXPORT_SYMBOL_GPL(flush_work);
【参考博客】
[1] Linux 内核设计与实现
[2] Linux内核 | 中断机制 - 世至其美
[3] INIT_WORK()工作队列使用-CSDN博客
[4] https://www.cnblogs.com/oceanding/p/7595738.html
相关文章:
Linux 中断处理
一、基本概念 1、中断及中断上下文 中断是一种由硬件设备产生的信号,不同设备产生的中断通过中断号来区分。CPU在接收到中断信号后,根据中断号执行对应的中断处理程序(Interrupt Service Routine) 内核对异常和中断的处理类似&a…...
人大金昌netcore适配,调用oracle模式下存储过程\包,返回参数游标
using KdbndpConnection conn new KdbndpConnection("Host192.168.133.221;Port54321;Databasedb1;Poolingtrue;User IDsystem;Password123");conn.Open();//存储过程调用也是类似using var cmd conn.CreateCommand();cmd.CommandText "模式.包名称.存储过程…...
pandas常用的一些操作
EXCLE操作 读取Excel data1 pd.read_excel(excle_dir) 读Excel取跳过前几行: data1 pd.read_excel(excle_dir,skiprows1) 获取总行数 data1.shape[0] 获取总列数 data1.shape[1] 指定某列数据类型 data1 pd.read_excel("C:数据导入.xlsx",dtype…...
【鸿蒙开发】系统组件Row
Row组件 Row沿水平方向布局容器 接口: Row(value?:{space?: number | string }) 参数: 参数名 参数类型 必填 参数描述 space string | number 否 横向布局元素间距。 从API version 9开始,space为负数或者justifyContent设置为…...
Hadoop和zookeeper集群相关执行脚本(未完,持续更新中~)
1、Hadoop集群查看状态 搭建Hadoop数据集群时,按以下路径操作即可生成脚本 [test_1analysis01 bin]$ pwd /home/test_1/hadoop/bin [test_01analysis01 bin]$ vim jpsall #!/bin/bash for host in analysis01 analysis02 analysis03 do echo $host s…...
蓝桥杯算法题:栈(Stack)
这道题考的是递推动态规划,可能不是很难,不过这是自己第一次靠自己想出状态转移方程,所以纪念一下: 要做这些题目,首先要把题目中会出现什么状态给找出来,然后想想他们的状态可以通过什么操作转移…...
JavaWeb-监听器
文章目录 1.基本介绍2.ServletContextListener1.基本介绍2.创建maven项目,导入依赖3.代码演示1.实现ServletContextListener接口2.配置web.xml3.结果 3.ServletContextAttributeListener监听器1.基本介绍2.代码实例1.ServletContextAttributeListener.java2.配置web…...
系统架构设计基础知识
一. 系统架构概述系统架构的定义 系统架构(System Architecture)是系统的一种整体的高层次的结构表示,是系统的骨架和根基,支撑和链接各个部分,包括构件、连接件、约束规范以及指导这些内容设计与演化的原理࿰…...
Vue自定义指令介绍及使用方法
介绍 除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外,Vue 还允许你注册自定义的指令 (Custom Directives)。 之前已经介绍了两种在 Vue 中重用代码的方式:组件 和 组合式函数。组件是主要的构建模块,而组合式函数则侧重于有状态…...
React 组件生命周期函数的用法和示例代码
React 中的生命周期函数可以分为三个阶段:Mounting(挂载),Updating(更新)和 Unmounting(卸载)。每个阶段都有不同的函数,用于执行不同的操作。 Mounting(挂载…...
【nginx运维】[emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)
关于nginx端口被占用的问题: If you get following error, when you try to start nginx… [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use) Then it means nginx or some other process is already using port 80. You can kill it using: su…...
浏览器工作原理与实践--虚拟DOM:虚拟DOM和实际的DOM有何不同
虚拟DOM是最近非常火的技术,两大著名前端框架React和Vue都使用了虚拟DOM,所以我觉得非常有必要结合浏览器的工作机制对虚拟DOM进行一次分析。当然了,React和Vue框架本身所蕴含的知识点非常多,而且也不是我们专栏的重点,…...
arm工作模式、arm9通用寄存器、异常向量表中irq的异常向量、cpsr中的哪几位是用来设置工作模式以及r13,r14,15别名是什么?有什么作用?
ARM 首先先介绍一下ARM公司。 ARM成立于1990年11月,前身为Acorn计算机公司 主要设计ARM系列RISC处理器内核 授权ARM内核给生产和销售半导体的合作伙伴ARM公司不生产芯片 提供基于ARM架构的开发设计技术软件工具评估版调试工具应用软件总线架构外围设备单元等等CPU中…...
电脑上音频太多,播放速度又不一致,如何批量调节音频播放速度?
批量调节音频速度是现代音频处理中的一个重要环节,尤其在音乐制作、电影剪辑、有声书制作等领域,它能够帮助制作者快速高效地调整音频的播放速度,从而满足特定的制作需求。本文将详细介绍批量调节音频速度的方法、技巧和注意事项,…...
pe格式从入门到图形化显示(十)-扩展最后一个节
文章目录 前言一、怎么扩展最后一个节?二、扩大节1.扩展节2.保存文件 前言 通过分析和解析Windows PE格式,并使用qt进行图形化显示 一、怎么扩展最后一个节? 在PE文件中,扩大最后一个节通常是通过修改PE文件头中的节表来实现的。…...
设计模式之创建型模式---建造者模式
文章目录 建造者模式概述经典的建造者模式建造者模式的变种总结 建造者模式概述 建造者模式是一种广泛使用的设计模式,在三方开源库和各种SDK中经常见到。建造者设计模式在四人帮的经典著作《设计模式:可复用面向对象软件基础》中被提及,它的…...
如何从零开始训练一个语言模型
如何从零开始训练一个语言模型 #mermaid-svg-gtUlIrFtNPw1oV5a {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-gtUlIrFtNPw1oV5a .error-icon{fill:#552222;}#mermaid-svg-gtUlIrFtNPw1oV5a .error-text{fill:#5522…...
Python 设计一个监督自己的软件1
基本要求:每做一件事,软件就会按照事情权重加相应的分数,总分数也会增加,要可视化页面 使用Python编写的一个简单的日常任务记录和评分系统,包括可视化页面。 首先,我们定义一个任务字典,其中包含各种日常任务及其对应的权重分数…...
商家转账到零钱权限开通操作攻略
商家转账到零钱是什么? 商家转账到零钱是微信商户号里的一个功能,很早以前叫企业付款到零钱。 从2022年5月18日,原“企业付款到零钱”升级为“商家转账到零钱”,已开通商户的功能使用暂不受影响,新开通商户可前往「产…...
【DAC‘ 2022】Kite: A Family of Heterogeneous Interposer Topologies
Kite: A Family of Heterogeneous Interposer Topologies Enabled via Accurate Interconnect Modeling 背景和动机 背景动机 工作内容 KITE 拓扑 实验方法和评估结果 Kite: A Family of Heterogeneous Interposer Topologies Enabled via Accurate Interconnect Modeling 通…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
