By:Ailson Jack
Date:2016.04.10
个人博客:www.only2fire.com
本文在我博客的地址是:http://www.only2fire.com/archives/63.html,排版更好,便于学习。
上一章简单的讲了一下中断的上半部(中断处理程序),这一章就讲讲中断的下半部以及下半部的几种实现机制,最后简单的写了几个测试的例子来测试软中断、tasklet和工作队列。
测试程序下载地址:。
1、下半部简述
中断下半部的任务是执行与中断处理密切相关但中断处理程序本身并不执行的工作。
对于中断中的任务应该在上半部还是下半部执行,并没有严格的规则来规定,但是还是有一些提示可供借鉴:
1)、如果一个任务对时间非常敏感,将其放在中断处理程序中执行;
2)、如果一个任务和硬件相关,将其放在中断处理程序中执行;
3)、如果一个任务要保证不被其他中断(特别是共享同一中断线的中断)打断,将其放在中断处理程序中执行;
4)、其他所有任务可以考虑放置在下半部执行。
和上半部只能通过中断处理程序实现不同,下半部可以通过多种机制实现。随着Linux内核的发展,产生了一些新的机制,也淘汰了一些旧的机制。目前,下半部主要有三种机制:软中断,tasklet,工作队列。
2、中断下半部机制 — 软中断
软中断是一组静态定义的下半部接口,在Linux-2.6.34内核中有10个,可以在所有处理器上同时执行(即使两个类型相同也可以)。软中断必须在编译期间就进行静态注册。
软中断保留给系统中对时间要求最严格以及最重要的下半部使用。目前,只有两个子系统(网络和SCSI)直接使用软中断。此外,内核定时器和tasklet都是建立在软中断上的。
软中断的代码在:kernel/softirq.c
使用软中断的流程图如下:
1)、分配索引
软中断目前有10种类型,软中断类型定义在include/linux/interrupts.h中,可以通过修改该文件中的枚举,来实现增加或者减少软中断,下面我增加一个自己定义的软中断AilsonJack_SOFTIRQ:
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 */
AilsonJack_SOFTIRQ,/*自己定义的一个新的软中断类型*/
NR_SOFTIRQS
};
接着在kernel/softirq.c文件中的softirq_to_name数组中给新增的软中断取个名字:
char *softirq_to_name[NR_SOFTIRQS] = {
“HI”, “TIMER”, “NET_TX”, “NET_RX”, “BLOCK”, “BLOCK_IOPOLL”,
“TASKLET”, “SCHED”, “HRTIMER”, “RCU”, “AilsonJack”
};/*增加一种新的softirq —> AilsonJack*/
索引号小的软中断在索引号大的软中断之前执行。
2)、注册处理程序
注册处理程序使用的是open_softirq()函数,它的定义在kernel/softirq.c文件中:
/*
* 将软中断类型和软中断处理函数加入到软中断序列中
* @nr – 软中断类型
* @ void (*action)(struct softirq_action *) – 软中断处理的函数指针
*/
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
/* softirq_vec是个struct softirq_action类型的数组 */
softirq_vec[nr].action = action;
}
struct softirq_action 的定义在 include/linux/interrupt.h 文件中:
/*
* 这个结构体的成员是个函数指针,函数的名称是action
* 函数指针的返回值是void型
* 函数指针的参数是 struct softirq_action 的地址,其实就是指向 softirq_vec 中的某一项
* 如果 open_softirq 是这样调用的: open_softirq(NET_TX_SOFTIRQ, my_tx_action);
* 那么 my_tx_action 的参数就是 softirq_vec[NET_TX_SOFTIRQ]的地址
*/
struct softirq_action
{
void (*action)(struct softirq_action *);
};
一个软中断不会抢占另一个软中断。实际上,唯一可以抢占软中断的是中断处理程序。不过,其他的软中断(甚至是相同类型的软中断)可以在其他处理器上同时执行。
软中断处理程序执行的时候,允许响应中断,但它自己不能休眠。这意味着你不能在软中断中使用信号量或者其他什么阻塞式函数。
3)、触发软中断
通过在枚举类型列表中添加新项以及调用open_sofirq()进行注册以后,新的软中断处理程序就能够运行。raise_softirq()函数可以将一个软中断设置为挂起状态,让它在下一次调用do_softirq()函数时投入运行。
触发软中断的函数raise_softirq()定义在kernel/softirq.c文件中:
/*
* 触发某个类型的软中断
* @nr – 被触发的软中断类型
* 从函数中可以看出,在处理软中断前后有保存和恢复本地中断的操作
*/
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
raise_softirq(NET_TX_SOFTIRQ),这样会触发NET_TX_SOFTIRQ软中断。
在中断处理程序中触发软中断是最常见的形式。
4)、执行软中断
在触发了软中断之后,系统会在合适的时刻让软中断运行,该函数不需要自己调用。
执行软中断do_softirq()定义在kernel/softirq.c文件中:
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
/* 判断是否在中断处理中,如果正在中断处理,就直接返回 */
if (in_interrupt())
return;
/* 保存当前寄存器的值 */
local_irq_save(flags);
/* 取得当前已注册软中断的位图 */
pending = local_softirq_pending();
/* 循环处理所有已注册的软中断 */
if (pending)
__do_softirq();
/* 恢复寄存器的值到中断处理前 */
local_irq_restore(flags);
}
文章的更详细的信息:本文在我博客的地址是:http://www.only2fire.com/archives/63.html,排版更好,便于学习。
注:转载请注明出处,谢谢!^_^