一文搞懂Linux内核中断机制原理与实现

为什么需要中断?

如果让内核定期对设备进行轮询,以便处理设备,那会做很多无用功,因为外设的处理速度一般慢于CPU,而CPU不能一直等待外部事件。所以能让设备在需要内核时主动通知内核,会是一个聪明的方式,这便是中断。

中断处理程序

在响应一个特定中断时,内核会执行一个函数——中断处理程序。中断处理程序与其他内核函数的区别在于,中断处理程序是被内核调用来响应中断的,而它们运行于我们称之为中断上下文的特殊上下文中。

中断处理程序就是普通的C代码。特别之处在于中断处理程序是在中断上下文中运行的,它的行为受到某些限制:

  • 1)不能向用户空间发送或接受数据
  • 2)不能使用可能引起阻塞的函数
  • 3)不能使用可能引起调度的函数

中断机制

Linux中断主要分为硬中断(IRQ)和软中断两类。

IRQ主要分为:短类型IRQ和长类型IRQ。短类型IRQ需要很短的时间,在此期间机器的其他部分被锁定,而且不能发生其他中断被处理。长类型IRQ需要较长的时间,期间可能发生其他中断。

当用户程序被来自外部信号中断后,立即保存现场工作,包括保存返回地址和用户寄存器等数据,然后查找中断向量表,找出相应的中断处理程序。系统将中断分为三种:捕俘、系统调用和外中断。捕俘:通过捕俘处理程序入口表查找到用户编写的处理程序执行。系统调用:软中断,通过系统调用表找到操作系统核心提供的服务例程。外中断:直接调用核心提供的外中断处理程序运行。

内核资料直通车:最新Linux内核源码资料文档+视频资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

硬中断过程

Linux中,若一个硬件想向CPU发送中断信号,必须首先获得一个可用的“中断请求线”(即中断前必须获得一个可用的IRQ号),产生一个中断信号后以电信号发送给中断控制器(硬件芯片),接着CPU根据中断控制器的状态位判定中断的来源,获得中断号,根据中断号查找中断向量表,从表中获得中断处理函数的地址,然后跳转到中断函数入口地址处,执行这个函数。

中断处理程序—硬中断

中断处理程序主要做的工作:

  • a.保护未被硬件保护的一些必须的寄存器
  • b.识别各个中断源,分析产生中断的原因
  • c.处理发生的中断事件
  • d.恢复正常的工作

Linux规定中断处理程序是不可重入的,指的是同一中断线上不可以再发生新的中断,因为所有的处理器都将原中断所在的中断线已经屏蔽。

Linux中同样规定了同一中断程序不能够并行,这样同一个中断处理程序不可以被同时调用来处理嵌套的中断。

Linux中将中断处理程序分为两部分:上半部和下半部。

上半部主要用来处理那些具有严格时限要求的任务。上半部可以看做是一个用来“登记 中断”功能的函数,将中断例程的下半部挂到下半部执行队列中。上半部要求执行很快,主要是因为上半部完全屏蔽中断下执行,即不可中断。

下半部主要用于处理那些可以稍后执行的任务。下半部是可中断的,当发生其他中断时,下半部可中断等待另外一个中断的上半部执行完毕后再继续执行。

上下半部机制

我们期望让中断处理程序运行得快,并想让它完成的工作量多,这两个目标相互制约,如何解决——上下半部机制。

我们把中断处理切为两半。中断处理程序是上半部——接受中断,他就立即开始执行,但只有做严格时限的工作。能够被允许稍后完成的工作会推迟到下半部去,此后,在合适的时机,下半部会被开终端执行。上半部简单快速,执行时禁止一些或者全部中断。下半部稍后执行,而且执行期间可以响应所有的中断。这种设计可以使系统处于中断屏蔽状态的时间尽可能的短,以此来提高系统的响应能力。上半部只有中断处理程序机制,而下半部的实现有软中断实现,tasklet实现和工作队列实现。

我们用网卡来解释一下这两半。当网卡接受到数据包时,通知内核,触发中断,所谓的上半部就是,及时读取数据包到内存,防止因为延迟导致丢失,这是很急迫的工作。读到内存后,对这些数据的处理不再紧迫,此时内核可以去执行中断前运行的程序,而对网络数据包的处理则交给下半部处理。

上下半部划分原则

  • 1) 如果一个任务对时间非常敏感,将其放在中断处理程序中执行;
  • 2) 如果一个任务和硬件有关,将其放在中断处理程序中执行;
  • 3) 如果一个任务要保证不被其他中断打断,将其放在中断处理程序中执行;
  • 4) 其他所有任务,考虑放置在下半部执行。

Linux中提供了三种机制来实现下半部机制:

(1)软中断

软中断是一组静态定义的下半部结构,使用数组来组织软中断结构体,共有32个。两个相同的软中断可以同时执行,必须在编译期间进行静态注册。

软中断机制一般都保留给系统中对时间要求最严格以及重要的下半部来使用。Linux2.6中只有两个子系统是通过软中断来实现的:网络子系统和SCSI

(2)tasklet

tasklet要比软中断机制方便且简单,而且它本身也是基于软中断实现,属于软中断,既可以静态地创建tasklet,也可以动态的创建tasklet。

Linux中tasklet分为两类:HI_SOFTIRQ和TASKLET_IRQ,前者比后者的优先级要高,优先调用前者。在中断数组irq_desc[]中会分配两项给tasklet,即两种类型各占数组中一项。两者分别以一个链表来组织。

(3)工作队列

(work queue)工作队列与前两者最大的不同之处是它是唯一一个能在进程上下文中运行的下半部机制,意味着它能允许睡眠。

工作队列的实质是将推后的工作交给一个内核线程来完成,核心思想即时创建一个内核线程,Linux中已经默认提供了一种命名为enents一类工作者线程来实现工作队列。

Linux软中断和工作队列的作用是什么:

Linux中的软中断和工作队列是中断上下部机制中的下半部实现机制。

1.软中断一般是“可延迟函数”的总称,它不能睡眠,不能阻塞,它处于中断上下文,不能进城切换,软中断不能被自己打断,只能被硬件中断打断(上半部),可以并发的运行在多个CPU上。所以软中断必须设计成可重入的函数,因此也需要自旋锁来保护其数据结构。

2.工作队列中的函数处在进程上下文中,它可以睡眠,也能被阻塞,能够在不同的进程间切换,以完成不同的工作。

可延迟函数和工作队列都不能访问用户的进程空间,可延时函数在执行时不可能有任何正在运行的进程,工作队列的函数有内核进程执行,他不能访问用户空间地址。中断的数据结构

Linux内核中定义了一个数组irq_desc[]数组来管理中断。数组中的每一项对应一个中断源。数组中的每个成员都为irq_desc_t结构体,即数组中的每一项对应着中断向量表中的一项。

中断的数据结构

Linux内核中定义了一个数组irq_desc[]数组来管理中断。数组中的每一项对应一个中断源。数组中的每个成员都为irq_desc_t结构体,即数组中的每一项对应着中断向量表中的一项。

(1)irq_desc_t结构体|irq_desc_t结构体用来描述中断源。其中结构体中的handler指向hw_interrupt_type结构体的指针,acTIon变量指向由irqacTIon结构体组成的单向链表的头的指针。

(2)irqacTIon结构体:该结构体中指明内核接收到特定IRQ后该才去的动作。结构体中变量handler指向中断处理程序。

(3)hw_interrupt_type结构体:用来描述中断控制器,是一个抽象的中断控制器。

中断上下文

当一个中断处理程序正在执行时,内核处于中断上下文中。中断上下文是不可以睡眠的。与进程上下文是不同的,进程上下文即使睡眠了也可以重新调度将其唤醒,中断上下文不可以被重新调度。 中断处理程序没有自己的堆栈,它会共享被它中断的那个进程的堆栈,如果没有进程正在执行,则占用idle进程的堆栈(每个处理器都有自己的运行队列,队列中都有idle进程,当前运行队列都dequeue时则运行idle进程)

Linux中断编程

  • 中断申请(申请IRQ)-----request_irq()
  • 释放IRQ------free_irq()

使能和屏蔽IRQ

disable_irq() disable_irq_nosync() enable_irq()

底半部机制:Linux实现底半部机制主要方式有 tasklet 工作队列 软中断

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

简说Linux内核

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值