linux内核后半段中断,Linux内核中的下半部机制之软中断(softirq)

文章系原创,如需转载请注明:转载自”Blog of UnicornX” (http://unicornx.github.io/)

最新更新于:2016-04-19

主要参考

LKD3

LDD3

本文的内核代码以2.6.32.6为准。

我们要知道的是,tasklet的实现基于软中断机制。所以首先要搞清楚软中断机制的原理,那么tasklet的实现机制也就非常简单了。

构成软中断机制的核心元素:

有关构成软中断机制的核心元素可以参考下图所示,这张图来自Linux中软中断机制分析。注意这篇文章认为执行软中断只有通过内核线程,而根据下文的分析这是不全面的,这里暂先不管它,这里只是通过这张图从整体上了解一下内核实现软中断时涉及到哪些重要的对象:

7f813638-29ad-4b9a-bdf2-0a0d3a2e6b35.JPG

软中断状态寄存器soft interrupt state(irq_stat)irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;

结构体irq_cpustat_t很简单,但这是一个和体系架构相关的结构体,通常情况下只包含一个成员:

typedef struct {

unsigned int __softirq_pending;

} ____cacheline_aligned irq_cpustat_t;

但对ARM架构包含两个:

typedef struct {

unsigned int __softirq_pending;

unsigned int local_timer_irqs;

} ____cacheline_aligned irq_cpustat_t;

但我们只关注其共有的那个成员__softirq_pending,其用于标识一个软中断当前发生了还是没有发生,每一个位标识一个软中断,因为它是int型,在32位机器上最多标识32个软中断类型。

irq_stat对象每个处理器一个。

软中断向量表(softirq_vec)static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

这是一个静态定义的结构体softirq_action的数组,每个被注册的软中断都占据该数组的一项,前面介绍irq_stat的时候我们已经看到以32位系统为例,最多支持32种软中断,目前系统定义了10个,具体可查看include/linux/interrupt.h,里面定义了一个enum如下:

enum

{

HI_SOFTIRQ=0,

TIMER_SOFTIRQ,

NET_TX_SOFTIRQ,

NET_RX_SOFTIRQ,

BLOCK_SOFTIRQ,

BLOCK_IOPOLL_SOFTIRQ,

TASKLET_SOFTIRQ,

SCHED_SOFTIRQ,

HRTIMER_SOFTIRQ,

RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */

NR_SOFTIRQS

};

其中:

NET_RX_SOFTIRQ和NET_TX_SOFTIRQ用于网络子系统的收发;

BLOCK_SOFTIRQ和BLOCK_IOPOLL_SOFTIRQ用于SCSI磁盘系统处理;

TIMER_SOFTIRQ和HRTIMER_SOFTIRQ用于定时器处理;

SCHED_SOFTIRQ用于任务调度;

RCU_SOFTIRQ用于RCU锁定;

和tasklet相关的就是剩下的HI_SOFTIRQ用于高优先级的taskket,TASKLET_SOFTIRQ用于正常优先级的taskket。

如果要新定义自己的软中断类型则需要修改enum,添加一项,但一般普通的驱动开发人员没有特殊的需要,不应该直接自己定义新的软中断类型,大部分的工作都可以基于tasklet开发。这里唯一要注意的是软中断的注册是在编译阶段静态分配的,后面我们会看到tasklet只是在这个基础上做了进一步的封装,允许动态地注册和注销。

结构体类型softirq_action定义如下,其成员很简单,就是一个函数指针,存放对应软中断发生时的处理函数的地址,需要在注册相应的软中断时给定。

struct softirq_action

{

void (*action)(struct softirq_action *);

};

注册软中断的方法是open_softirq()。

软中断守护内核线程ksoftirqd

即上图中所谓的Softirq-daemon。有关这些线程的作用参考LKD3的“8.3.2 使用taskket”的“4. ksoftirqd”部分。

Linux中软中断实现机制分析

如果要使用软中断,大致经历如下步骤,前面说过一般情况驱动开发人员无需自己新定义软中断,这里重点理解第三步以后软中断触发后的处理过程:

第一步:新增软中断类型。

修改include/linux/interrupt.h文件中的enum,新增一项。

第二步:通过open_softirq函数注册软中断处理函数。

编写软中断处理函数时需要注意的是:

软中断处理函数和硬中断处理函数同样运行在中断上下文中,所以该函数里同样不允许休眠。

但其和硬中断处理函数有个区别就是软中断处理函数执行的时候,允许响应中断。

当一个软中断处理程序运行时,当前处理器上的其他的软中断被禁止,也就是说一个软中断不会抢占另外一个软中断,唯一可以抢占软中断的是中断处理程序(即我们所谓的硬中断)。但如果你的系统是多处理器的话,其他类型的软中断,甚至是相同类型的软中断可以在别的处理器上同时运行,这意味着任何共享数据都需要严格的锁保护。这一点更多的描述,参考LKD3的8.2.2章节的”2.注册你的处理程序”。后面我们会看到tasklet在封装软中断的时候就是在这一点上加上了限制,即同一个tasklet处理程序是不允许在多个处理器上同时运行的。封装使我们使用tasklet这种软中断时变得简单,但功能上受到了限制。

第三步:触发软中断。

在恰当的时间点上调用raise_softirq函数将irq_stat对应的bit置位,即所谓的设置为挂起(pending)状态。触发的时间点一般发生在我们所期望的上半部分,比如一个中断处理函数中(irq_handler_t类型的回调函数,参考Linux内核驱动之Linux中断处理子系统(基于v2.6.32.6和s3c2440)中的中断处理子系统章节的irqaction)。

第四步:执行软中断。

在一个合适的时刻,待处理(已经被raise的)的软中断会被检查和执行,这些时间点包括如下几种情况:

从一个中断处理程序(irq_handler_t类型的回调函数)返回退出中断上下文时。这是最常见的执行点。

在ksoftirqd内核线程中,当出现大量的软中断需要处理时,会唤醒一组内核线程来辅助处理。详细的内容请参考LKD3的“8.3.2 使用taskket”的“4. ksoftirqd”部分。

在那些显式检查并执行软中断的代码中,比如网络子系统。

不管是什么时间点,都会调用do_softirq(),该函数很简单,通过判断irq_stat的值是否为0来判断是否有pending的软中断,如果有则对pending的软中断调用softirq_vec中对应的软中断的处理程序action。具体函数有点复杂,可以参考LKD3的简化版本,8.2.1章节。

如上所述。

软中断的工作工程模拟了实际的中断处理过程,当某一软中断触发后,首先需要设置irq_stat中对应的中断标记位,触发中断事务处理,无论是哪种方式执行do_softirq(),都会通过查询发现是某一软中断事务发生,那么通过软中断向量表softirq_vec调用软中断服务程序action()。这就是软中断的过程,与硬件中断唯一不同的地方是从“”触发软中断“”到“执行软中断”中找到服务程序的映射过程。在CPU的硬件中断发生之后,CPU需要将硬件中断请求通过向量表映射成具体的服务程序,这个过程是硬件自动完成的,但是软中断不是,其需要软件触发(raise_softirq),并通过do_softirq()去实现这一查找映射过程,这也就是软件模拟的中断,故称之为软中断。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值