Linux内核学习之中断处理

news2024/11/15 15:27:11

Linux内核学习之中断处理

  • 0 前言
  • 1 中断处理程序的嵌套执行
  • 1 Linux对x86异常的处理
  • Linux中向量用途
  • 1 Linux中的中断门描述符
  • Linux中的中断描述符
  • 硬中断
  • 软中断和tasklet
    • 软中断
    • tasklet[^2]
    • ksoftirqd内核线程
    • kworker内核线程

0 前言

文本基于x86架构讲解Linux中对中断的处理,因此本文假定你已经阅读了x86架构学习之中断,了解了x86硬件中断的结构和行为。

1 中断处理程序的嵌套执行

  • 每个中断处理程序都代表一个内核控制路径,中断的嵌套执行(抢占),同时也意味着内核控制路径的嵌套执行,一个操作系统如果要保证高效的响应和IO,就必须允许中断嵌套。这也就意味着,Linux在系统运行的大部分时刻,都是开中断的。
  • 中断处理程序永远不会被用户态任务阻塞,这也意味着,在处理中断的时候,是不允许发生任务切换的。
  • 一个设计好、并经过大量测试的Linux内核,除了个别特定用途的异常,是不会在内核态产生异常的。异常要么由编程错误(如除以0)产生,要么由编程异常(int n指令)或调试指令(debug、bound指令)产生。除了缺页异常 ,Linux内核没有编程错误(如果没有bug),同时也不会调用产生异常的显式指令。缺页异常 被内核用于内存交换,并且被设计成绝不会再嵌套产生异常,因此内核态中,最多有两个嵌套异常(系统调用+缺页)。

1 Linux对x86异常的处理

中断号异常异常处理程序关联的Linux信号
0diveide errordevide_error()SIGFPE
1debugdebug()SIGTRAP
2NMInmi()NONE
3break pointint3()SIGTRAP
4overflowoverflow()SIGSEGV
5bound checkbounds()SIGSEGV
6invalid opcodeinvalid_op()SIGILL
7device not availabledevice_not_available()NONE
8double faultdoublefault_fn()NONE
9coprocessor segment overruncoprocessor_segment_oversun()SIGFPE
10invalid TSSinvalid_tss()SIGSEGV
11segment not presentsegment_not_present()SIGBUS
12stack exceptionstack_segment()SIGBUS
13general exceptiongeneral_protection()SIGSEGV
14page faultpage_fault()SIGSEGV
15intel reservednone()NONE
16float point errorcoprocessor_error()SIGFPE
17alignment checkalignment_check()SIGSEGV
18machine checkmachine_check()NONE
19SIMD float pointsimd_coprocessor_error()SIGFPE

Linux中向量用途

向量范围用途
0~31intel预定义的异常或保留
32~127外部中断
128用于系统调用
129~238外部中断
239本地APIC中断
240本地APIC高温中断
241~250Linux保留
251~253处理器间中断
254本地APIC错误中断
255本地APIC伪中断

1 Linux中的中断门描述符

x86架构中,被中断用到的中断描述符被其称为中断门描述符,Linux则进一步对这个门描述符进行细分类:

  • 中断门。用户态进程无法访问的x86中断门(通过设置DPL字段为0),所有的Linux中断处理程序都通过中断门激活,并且全部限制在内核态。
  • 系统陷阱门。用户态可以访问的一个x86陷阱门(设置DPL为3),通过系统门来激活三个Linux异常处理程序,它们的向量(中断号)是4、5、128,即,用户态下,可以使用into, bound, int 0x80三条指令,int 0x80是Linux中的系统调用。
  • 系统中断门。能够被用户态进程访问的x86中断门(设置DPL为3),与向量3相关的异常处理程序是由系统中断门激活的,因此,用户态下可以使用int3指令。
  • 陷阱门。用户态进程不能访问的一个x86陷阱门(设置DPL为0),大部分Linux异常处理程序都通过陷阱门来激活。
  • 任务门。用户态无法访问的x86任务门(设置DPL为0),Linux对double fault异常处理程序通过任务门激活。

刚上电时,CPU运行在实模式,IDT由BIOS初始化使用,之后由Linux接管重新初始化。内核代码中涉及初始化中断的描述符的代码如下:

/*  设置门描述符,gete:中断号,type:门类型,addr:中断程序入口逻辑地址,
    dpl:描述符特权级,ist:中断栈索引,具体参考前言中文章关于ist的描述 */
static inline void _set_gate(int gate, unsigned type, void *addr,
                 unsigned dpl, unsigned ist, unsigned seg)
{
    gate_desc s;
    pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg);
    write_idt_entry(idt_table, gate, &s);
}
// 设置中断门:GATE_INTERRUPT + DPL=0
static inline void set_intr_gate(unsigned int n, void *addr)
{
    BUG_ON((unsigned)n > 0xFF);
    _set_gate(n, GATE_INTERRUPT, addr, 0, 0, __KERNEL_CS);
}
// 设置系统中断门:GATE_INTERRUPT + DPL=3
static inline void set_system_intr_gate(unsigned int n, void *addr)
{
    BUG_ON((unsigned)n > 0xFF);
    _set_gate(n, GATE_INTERRUPT, addr, 0x3, 0, __KERNEL_CS);
}
// 设置系统陷阱门:GATE_TRAP + DPL=3
static inline void set_system_trap_gate(unsigned int n, void *addr)
{
    BUG_ON((unsigned)n > 0xFF);
    _set_gate(n, GATE_TRAP, addr, 0x3, 0, __KERNEL_CS);
}
// 设置陷阱门:GATE_TRAP + DPL=0
static inline void set_trap_gate(unsigned int n, void *addr)
{
    BUG_ON((unsigned)n > 0xFF);
    _set_gate(n, GATE_TRAP, addr, 0, 0, __KERNEL_CS);
}
// 设置中断门:GATE_TASK + DPL=0
static inline void set_task_gate(unsigned int n, unsigned int gdt_entry)
{
    BUG_ON((unsigned)n > 0xFF);
    _set_gate(n, GATE_TASK, (void *)0, 0, 0, (gdt_entry<<3));
}

Linux中的中断描述符

Linux中用另一种描述符来描述中断状态和响应程序入口,在中断发生时,先通过intel的中断描述符表找到中断程序最初始的入口,在该程序中,Linux再访问自己配置号的中断描述符表,从表中提取指定中断号的描述符数据结构irq_desc,irq_desc中包含中断状态,以及中断处理程序,其原文字段含义如下。

fielddescription
irq_dataper irq and chip data passed down to chip functions
kstat_irqsirq stats per cpu
handle_irqhighlevel irq-events handler
preflow_handlerhandler called before the flow handler (currently used by sparc)
actionthe irq action chain
statusstatus information
core_internal_state__do_not_mess_with_itcore internal status information
depthdisable-depth, for nested irq_disable() calls
wake_depthenable depth, for multiple irq_set_irq_wake() callers
irq_countstats field to detect stalled irqs
last_unhandledaging timer for unhandled count
irqs_unhandledstats field for spurious unhandled interrupts
threads_handledstats field for deferred spurious detection of threaded handlers
threads_handled_lastcomparator field for deferred spurious detection of theraded handlers
locklocking for SMP
affinity_hinthint to user space for preferred irq affinity
affinity_notifycontext for notification of affinity changes
pending_maskpending rebalanced interrupts
threads_oneshotbitfield to handle shared oneshot threads
threads_activenumber of irqaction threads currently running
wait_for_threadswait queue for sync_irq to wait for threaded handlers
dir/proc/irq/ procfs entry
nameflow handler name for /proc/interrupts output

现在只需重点关注handle_irq、action、status三个字段:

  • handle_irq指向服务于整个中断线的处理程序;
  • action是一个链表,服务于共享该中断线的每个设备;
  • status是中断处理状态,目前有三种状态,空闲、挂起、处理中、禁用。

当硬件发生一个中断信号时,从硬件到Linux软件的处理流程如下:

请添加图片描述

硬中断

这个概念是针对中断紧急程度提出的,也是相对后文软中断而言的,指的是中断的实际处理程序就放在中断处理函数当中。在Linux中这种中断是紧急的(critical)或非紧急的(noncritical)。

紧急硬中断:

  • 中断实际处理程序在中断上下文,并在中断返回前执行完。
  • 中断执行期间,由硬件屏蔽其他中断(针对当前CPU)。紧急硬中断使用的是intel的中断门, 中断通过中断门进入中断处理程序时,会清理eflags.if标志位,即屏蔽中断1

非紧急硬中断:

  • 中断实际处理程序在中断上下文,并在中断返回前执行完。
  • 中断执行期间,中断是开启的(针对当前CPU),能够被高优先级中断打断(不能被同优先级打断)。非紧急硬中断使用的是intel的陷阱门,中断通过陷阱门进入中断处理程序时,不会清理eflags.if标志位,即保持中断开启。

软中断和tasklet

软中断即相对硬中断而言的,即非紧急可延迟中断(noncritical deferrable),指的是中断的实际处理程序没有放在中断处理函数当中,而是放在了中断返回之后。中断处理函数只负责简单的“应答”操作。推迟的中断处理被 Linux放在了专用的内核线程当中。

软中断

硬件中断信号发生时,触发触发中断处理函数的执行,硬中断会在中断上下文立即处理中断,而对于软中断,实际处理程序不在中断上下文,而被推迟到设计好的执行点执行。

  • 软中断执行主体不在中断上下文当中,但仍在内核态下执行。
  • 软中断使用的是intel的陷阱门,即开中断,不同于非紧急硬中断的是,软中断的实际处理程序由于放在中断上下文外,因此是可被低优先级中断打断的,但是,如果这个低优先级中断也是软中断,那么其实际处理程序的执行还是要经过排队的。
  • 软中断是可重入的。
  • 软中断是静态分配的(编译时)。

Linux为软中断分配了一个长度为32的数组(softirq_vec[32]),数组中每个元素(softirq_action)对应一个软中断,目前(3.10.89)仅使用9个。
Linux中使用的软中断(非最新内核):

软中断下标(优先级)说明
HI0处理高优先级tasklet
TIMER1时钟中断相关tasklet
NET_TX2网卡发包
NET_RX3网卡接包
BLOCK4
BLOCK_IOPOLL5
TASKLET6处理常规tasklet
SCHED7内核调度中断
HRTIMER8
RCU9RCU 锁中断
reserved10-31保留

查看系统每CPU软中断运行次数:

[root@localhost ~]# cat /proc/softirqs
                    CPU0       CPU1       CPU2       CPU3
          HI:          2          0         44          0
       TIMER:   74161634  108560055  104975829   97259447
      NET_TX:          0          0          0          1
      NET_RX:    1611551    1540001    1480112    1868864
       BLOCK:     692874     288353     242228     223828
BLOCK_IOPOLL:          0          0          0          0
     TASKLET:        493          0        396          0
       SCHED:   21956594   24238388   22492040   20494488
     HRTIMER:          0          0          0          0
         RCU:   49602186   71933466   69620972   64742279

tasklet2

tasklet是在软中断的基础上设计出来新子类,在上文中,HI 和 TASKLET 被用于tasklet,前者表高优先级的 tasklet,后者表低优先级的tasklet。
tasklet以拉链的形式挂载在 HI 和 TASKLET 上

请添加图片描述

每个节点是个tasklet任务,tasklet任务具有如下特点:

  • 相同的类型tasklet被设计成串行执行的,因此不必是可重入的。
  • 同类tasklet不可同时在不同CPU上执行(保证同类串行)
  • 不同类tasklet可以同时在不同CPU上执行。

ksoftirqd内核线程

前面说过,软中断被推迟到设计好的执行点执行,这个执行点就是内核线程 ksoftirqd,Linux为每个CPU都创建的一个ksoftirqd线程3,命名为 ksoftirqd/n,n为cpu逻辑号,ksoftirqd 被定时器周期性唤醒,也会别任意一次中断处理函数的退出流程唤醒(irq_exit())。

从ps命令hanksoftirqd/n

kworker内核线程

这个线程不处理中断,但是,做的工作和 ksoftirqd 相似,都是做延时任务,不同的是,kworker负责的是内核延迟函数,而不是中断延迟函数,另一个不同点是,每个CPU不仅有一个kworker线程,还有一个各自的工作队列,这和tasklet是不同的,tasklet只有固定的两个高低优先级队列。各个工作队列之间如果出现严重不均衡时,kworker还负责调整队列。


  1. 即使是关了中断,在多核处理器上,该中断仍然会被分发到其他CPU执行,因此,如果中断处理函数如果涉及临界数据,仍然需要加锁。 ↩︎

  2. tasklet多被用来编写IO驱动程序。 ↩︎

  3. 从用户的视角看(ps),ksoftirqd 就是一个进程,拥有自己的PID,但是,从内核角度看,所有内核线程都是共享内核地址空间的,我们知道进程的概念需要实现地址空间隔离,并没有把ksoftirqd称为进程,而是线程,其他内核线程也是同样。 ↩︎

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

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

相关文章

Telink泰凌微如何添加Lib库

基于TLSR8258 SDK&#xff1a;b85m_ble_single_connection_sdk 1.首先把lib文件放在sdk的proj_lib文件夹中 2. properties - c/c build - Settings - TC32 C Linker - Libraries&#xff0c;把文件添加到编译设置中。注意添加的库文件名需要删除“lib前缀”。例如&#xff1…

外贸管理软件一般都有哪些功能

外贸管理软件通常被设计来帮助国际贸易企业高效管理其业务流程。这类软件的功能多样&#xff0c;这里以神卓外贸管理软件为例&#xff0c; 以下是一些常见的核心功能模块&#xff1a; 客户关系管理 (CRM) 客户信息管理询盘与报价管理销售机会跟踪 订单管理 订单生成与处理发货…

Codeforces Round 916 (Div. 3) E1. Game with Marbles(博弈论*1400)

感觉很难想。 如果你直接想的话&#xff0c;你就会发现有很多做法可以选择&#xff0c;而你根本不知道应该选哪个。 这时候可以先假设鲍勃已经取走了爱丽丝的所有的颜色的弹珠&#xff0c;&#xff08;并且以每个颜色一个弹珠的代价&#xff09;。 这时候每一项得分就是 S i …

Linux简单介绍(2)

四、软件管理机制 4.1 Linux软件管理介绍 有一个很好的软件生态圈支持&#xff0c;才是一个优秀、值得广泛使用的操作系统平台。比如PC端的window操作系统、mac操作系统&#xff0c;手机端的IOS系统&#xff0c;Android系统等。在这些操作系统上安装软件&#xff0c;方便的不能…

思特威-秋招正式批-笔试

1.在全局数据区中分配空间的变量类型有哪些 2.new和malloc的区别 3. class CData{unsigned short m_uilndex, m_uilndexFlag 9; int m_iData[10]; int m_iType;int iGetDataType() {return m_iType;} public: CData(); }CData::CData(), m_iType(5) {string strTxt "…

自修C++Primer----3.4迭代器(iterator)的介绍

目录 1.迭代器的使用 1.1迭代器运算符 1.2从一个元素指向下一个元素 1.3迭代器的类型 1.4begin和end操作符 1.5解引用操作符和成员访问 1.6引入迭代器失效 1.7全部改成大写的一个练习 2.使用迭代器运算 我们可以使用下标运算符访问string里面的字符或者是这个vector里面…

浅谈二分算法

浅谈二分算法 二分 首先知道一下二分是什么。 二分&#xff0c;是一种快速处理大型数据的方法。基本逻辑是折半查找。 设有一个共有 n n n 个数字的数组&#xff0c;要从中查询某个元素&#xff0c;就可以用二分查找。 注&#xff1a;这里的数组默认其成员数值具有单调性…

C++类和对象(总篇)

文章目录 C类和对象1、类的定义1.1类定义格式1.2访问限定符1.3类域 2、实例化2.1实例化概念 3、this指针4、类的默认成员函数5、构造函数5.1构造函数的特点5.2实例分析 6、析构函数6.1析构函数的特点6.2实例分析 7、拷贝构造函数7.1 拷贝构造函数的特点7.2实例分析7.3浅拷贝和深…

Linux中断管理

Linux 内核提供了完善的中断框架,我们只需要申请中断,然后注册中断处理函数即可,使用非常方便,不需要一系列复杂的寄存器配置。 1.Linux中断简介 中断号 每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中断线。在 Linux 内核中使用一个 int 变…

神经网络—卷积层

1.讲解 Conv2d out_channels 参数为2时&#xff0c;会生成两个卷积核&#xff0c;分别与输入进行卷积。得到的两个输出为输出 新生成的卷积核和原来的卷积核不一定相同 in_channels (int) – Number of channels in the input image out_channels (int) – Number of channels…

Marching Cubes 算法再探

Marching Cubes 算法再探 基础过程代码mian.cppMarchingCubes.hMarchingCubes.cpp 之前做NeRF相关工作时简单看过&#xff0c;但没有深究其实现&#xff0c;知其然不知其所以然的程度&#xff0c;算是初探。 基础 为了对MC有大致了解&#xff0c;可以先根据Marching Cubes 算法…

两个实用的Python编程技巧

一、变量类型声明技巧 虽然在Python中可以不用声明变量的类型&#xff0c;但是为了加快程序的运算速度&#xff0c;减少不必要的bug&#xff0c;我们可以在定义变量之初就把它的类型确定&#xff0c;这样可以更好地传输变量值。如下面的例子。 我们定义了两个变量&#xff0c…

IO进程线程 240826作业

作业 创建3个进程,子进程1拷贝文件的前一半,子进程2拷贝后一半文件,父进程回收两个子进程资源。 将1.txt内容拷贝到2.txt中 #include <myhead.h> int main(int argc, const char *argv[]) {pid_t pid1;pid1 fork();int fd1 open("./1.txt",O_RDWR);if(fd1…

JavaWeb JavaScript ④ JS的对象和JSON

只要你的风格是适应客观规律的&#xff0c;那你以后会越来越好&#xff0c;做事情会越来越顺利 —— 24.8.26 一、JS创建对象 语法 方式1 new Object() 方式2 {属性名&#xff1a;属性值&#xff0c;… …&#xff0c;函数名&#xff1a;function(){}} 方式…

Python | Leetcode Python题解之第371题两整数之和

题目&#xff1a; 题解&#xff1a; MASK1 4294967296 # 2^32 MASK2 2147483648 # 2^31 MASK3 2147483647 # 2^31-1class Solution:def getSum(self, a: int, b: int) -> int:a % MASK1b % MASK1while b ! 0:carry ((a & b) << 1) % MASK1a (a ^ b) % MA…

Agent实际落地的应用 未来生活的无形助手

在这个信息爆炸的时代&#xff0c;我们每个人都在追求更高效的生活方式。想象一下&#xff0c;如果有一个无形的助手&#xff0c;能够理解我们的需求&#xff0c;自动处理繁琐的任务&#xff0c;甚至为我们提供个性化的建议&#xff0c;那将是多么美好的体验&#xff01;这正是…

线性DP经典题型

数字三角形&#x1f342; #include<bits/stdc.h> using namespace std; int main() {int n;cin>>n;vector<vector<int>>arr(n1,vector<int>(n1,0));for(int i 1;i<n;i){for(int j 1;j<i;j){cin>>arr[i][j];}}vector<vector<i…

【精选】分享9款AI毕业论文生成初稿题目网站

在当今学术研究领域&#xff0c;AI技术的应用日益广泛&#xff0c;尤其是在学术论文的撰写过程中。AI论文生成器的出现&#xff0c;极大地简化了学术写作流程&#xff0c;提高了写作效率。以下是9款推荐的AI毕业论文生成初稿的网站&#xff0c;它们各有特色&#xff0c;能够满足…

【Qt】多元素控件QTableWidget

多元素控件QTableWidget 使用QTableWidget表示一个表格控件&#xff0c;一个表格中包含若干行、每一个行又包含若干列。 表格中的每一个单元格&#xff0c;都是一个QTableWidget对象。 QTableWidget核心方法 方法说明 item(int row, int column) 根据⾏数列数获取指定的 Q…

【电路笔记】-运算放大器基础

运算放大器基础 文章目录 运算放大器基础1、概述2、运算放大器表示3、开环增益(Open-Loop Gain)4、输入和输出阻抗5、带宽6、偏移电压7、理想运算放大器模型7.1 饱和模式7.2 线性模式8、实际运算放大器9、总结1、概述 本文将介绍运算放大器(也称为运算放大器,Operational …