linux中断之下半部

欢迎进入IT技术社区论坛,与200万技术人员互动交流 >>进入

1.中断是内核不可缺少的一部分,但是中断处理程序本身存在一些局限性,

a.中断方式以异步方式执行并且有可能会打断期待其他重要代码,甚至包括其它中断处理程序的执行,因此为了避免被打断的代码停止的时间过长,中断处理程序应该执行得越快越好。

b.如果当前有一个中断处理程序在运行,在最好的情况下(如果设置了SA_INTERRUPT),与该中断同级的其它中断会被屏蔽,在最坏的情况下,当前处理器上所有其它中断都会被屏蔽,导致丢失中断,因此应当让他们执行得越快越好。

c.由于中断处理程序往往需要对硬件进行操作,所以他们通常有很高的时限要求。

d.中断处理程序不在进程上下文中运行,所以他们不能被阻塞。这限制了他们所做的事情。

内核将中断分为上下两个部分,第一部分是中断处理程序,用来完成对硬件中断的即使响应,第二部分就是我们所说的下半部,完成余下的相对宽松的任务。

上下部分的具体划分:

a.如果一个任务对时间非常敏感,将其放在中断处理程序中运行

b.如果一个任务和迎接爱你相关,放到中断处理程序

c.如果一个任务要保证不被其它中断特别的相同的中断打断,将其放到中断处理程序

d.余下任务放到下半部

中断程序在运行当中,当前的中断线在所有处理器上都会被屏蔽,如果一个处理程序是SA_INTERRUPT类型,它执行的时候还会禁止所有本地中断,把本地中短线全局地屏蔽掉,缩短中断被屏蔽的时间对系统的响应能力和性能至关重要。下半部执行的关键在于他们运行的时候,允许响应中断。

2.下半部的实现方式

2.6中内核提供了三种不同形式的下半部实现机制,软中断,tasklet和工作队列。还有一个特殊的延迟工具内核定时器。

tasklet通过软中断形式实现。利用了一个软中断进入口。

3.软中断

软中断在编程的时候金泰分配,有softirq_action结构表示,在<linux/interrupt.h>中,

struct softirq_action {

void (*action)(struct softirq_action *);

void *data;

}

kernel/softirq.c中定义了一个包含32个该结构的数组。

static struct softirq_action softirq_vec[32];

由此可知最多有32个软中断。

软中断处理程序原型如下:

void softirq_handler(struct softirq_action *);

一个软中断不会抢占另外一个软中断,唯一可抢占软中断的是中断处理程序,不过其它的软中断,或者相同类型的中断是可以在其它处理器上同时运行的。

软中断的执行,触发;

一个注册的软中断必须在被标记后才会执行,这被称作触发软中断,中断处理程序会返回前标记它的软中断,使其

稍后被执行,于是在合适的时刻,该软中断就会运行,这些时刻包括如下这些:

a.从一个硬件中断处理代码返回时

b.zai ksoftirqd内核线程中

c.在显示检查和执行待处理的软中断代码中,如网络子系统中。

这些方法最终都是调用do_softirq()也就是,触发软中断一定要调用该函数。do_softirq()会循环遍历每一个调用他们的处理程序。

软中断的使用

软中断保留给系统中时间要求最严格最重要的下半部,目前只有两个子系统–网络和scsi–直接使用软中断,

此外,内核定时器和tasklet都是简历在软中断上的,如果你想加入一个软中断,首先要考虑tasklet为什么不可以。

编译期间可以通过<linux/interrupt.h>中定义一个枚举类型来静态地声明软中断,内核用从0开始表示一种相对优先级,索引号小的软中断在索引大的软中断之前运行。

已有的tasklet类型,

HI_SOFTIRQ //0,优先级高的tasklet

TIMER_SOFTIRQ //1,定时器的下半部

NET_TX_SOFTIRQ //2,发送网络数据包

NET_RX_SOFTIRQ //3,接收网络数据包

SCSI_SOFTIRQ //4,SCSI的下半部

TASKLET_SOFTIRQ //5,tasklet

可以选择定义优先级在这几个中间,一般插在网络之后最后一项之前。

软中断的注册,

open_softirq(索引号,处理函数,相关数据);

软中断处理程序执行时候,允许响应中断,但它自己不能睡眠,在一个处理程序运行的时候,当前处理器上的软中断被禁止。其它处理器仍可以执行别的软中断。实际上如果同一个软中断在它被执行的时候再次被触发,那么另外一个处理器可以同事运行其处理程序,因为进入接口只有一个,所以任何共享数据即使内部的全局变量都要严格保护。

tasklet本身也是软中断,只不过同一个处理程序的多个实例不能在多个处理器上运行。

raise_softirq()可以将一个软中断设置为挂起,让他在下次调用do_softirq时候运行。

该函数在触发一个软中断之前要先禁止中断,触发后在回复回原来的状态,如果中断本来就被禁止了,可以使用raise_softirq_irqoff.

tasklet

tasklet是利用软中断实现的一种下半部机制,在执行频率很高和连续性要求很高的情况下使用软中断,否则请使用tasklet。

tasklet本身也是软中断,tasklet由tasklet_struct结构表示,每个结构体代表一个tasklet,在<linux/interrupt.h>中定义:

struct tasklet_struct{

struct tasklet_struct *next; //下一个tasklet

unsigned long state; //状态,0,TASKLET_STATE_SCHED,TASKLET_STATE_RUN.

atomic_t count; //引用计数,如果不为0则tasklet被禁止,不允许执行,如果为0,激活

void (*func)(unsigned long); //tasklet处理函数

unsigned long data; //处理函数参数

}

结构tasklet_vec代表普通tasklet,tasklet_hi_vec代表优先级高的tasklet,tasklet由tasklet_schedule()和

tasklet_hi_schedule()函数进行调度,他们接受一个指向tasklet_stuct结构的指针作为参数,看一下

tasklet_schedule()细节,

1.检查tasklet转台是否为tasklet_state_sched,如果是说明tasklet已经被调度过了,函数立即返回,此时可能

该tasklet被调度但还没有被执行。

2.保存中断状态,禁止本地中断。保证数据不会弄乱。

3.把要调度的tasklet加到对应的tasklet_VEC或者tasklet_hi_vec中去

4.唤起TASKLET_SOFTIRQ或者TASKLET_IRQ软中断,下次就会执行do_softirq调用到

5.恢复中断

其中tasklet_action()和tasklet_hi_action()是tasklet处理的核心:

1.禁止中断,

2.将当前处理器上的该链表清空

3.允许响应中断

4.循环遍历获得链表上的每一个待处理的tasklet

5.多处理器通过检测tasklet_state_run来判断是否在其它处理器上运行,如果运行那么现在该处理器不运行,同一 时间相同的tasklet只执行一个

6.如果当前的tasklet没有执行,将其状态标志位tasklet_state_run这样别的处理器就不会执行他

7.检查count是否为0,如果被禁止,跳到下一个

8.执行tasklet

9.清除tasklet的state状态

10.重复执行下一个tasklet,直到没有剩余的等待处理的tasklet。

所有的tasklet都是通过HI_SOFTIRQ和TASKLET_SOFTIRQ这两个软中断实现,在他们之上又延伸了一层。

tasklet的使用:

1.声明你的tasklet

可以静态也可以动态

命运如同手中的掌纹,无论多曲折,终掌握在自己手中。

linux中断之下半部

相关文章:

你感兴趣的文章:

标签云: