分类: LINUX
2009-10-06 11:44:27
提出软中断的原因: 中断嵌套。如果允许中断服务程序再次被中断(set_irq(n,xx), 其中xx结构置为(action,SA_INTERRUPT),代表actio可以被中断);或者不允许全程关中断,都会过犹不及。
因此,产生了bottom half机制。
全局数组bh_base,32个元素,当需要执行一个bh函数时,通过mark_bh将unsigned long bh_active中的某一位设成1,如果bh_mask的相应位也是1,即允许中断调用,则每次在do_IRQ执行完之后,会调用do_bottom_half执行bh函数。
softirq_init对内核的软中断初始化:
{
for(i=0;i<32;++i)
tasklet_init(bh_task_vec+i,bh_acton,i);
open_softirq(TASKLET_SOFTIRQ,tasklet_action,NULL);
open_softirq(HI_SOFTIRQ,tasklet_hi_action,NULL);
}
bh_task_vec是全局tasklet_struct结构数组
tasklet_init(t,f)
{
t->func = f;
}
对于其他软中断的初始化,则是:
open_softirq(nr,action)
{
softirq_vec[nr].action = action;
}
softirq_vec也是一个32个元素的数组,同时,还有一个以CPU为编号的tasklet_hi_vec[],里面有一个tasklet_struct队列的头指针,即每个CPU有一个tasklet_struct结构数组
回到tasklet机制,tasklet的function指针,指向bh_action,但并未挂载实际的处理程序。具体的bh函数是通过init_bh设置的,如后面要讲的时钟中断,
Sched.c:
Init_bh(TIMER_BH,timer_bh);
{
bh_base[nr]=xx_bh;
}
其中,bh_base即是会被执行的函数指针数组。
当需要执行一个特定bh函数时,通过
mark_bh(nr)àtasklet_hi_schedule(bh_task_vec+nr)
{
tasklet_hi_vec[cpu].list = bh_task_vec+nr;
cpu_raise_softirq
}àcpu_raise_softirq(cpu,nr)
{
softirq_active(cpu)|=1<
}
将处理函数挂接到tasklet_hi_vec结构上,同时将该cpu对应软中断标志位置1
到目前为止,软件消息发送过程结束,接下来看内核什么时候对该消息进行处理。
软中断,非tasklet执行方式:
内核每当在do_IRQ执行完毕一个中断服务程序后,进入软中断:
If(softirq_activecpu)&softirq_mask(cpu))
do_softirq();
do_softirq()
{
active = softirq_active(cpu)&mask
if(active)
{
do{
if(active&1)
h->action(h);
h++;
active>>1;
}while(active)
}
}
在上述代码中,内核检测CPU的active标志,看哪些nr位被置1,从而执行相应的action;在这里,h指针,和active最低位,是一一对应的,即他们会同时变化。
do_softirq()àsoftirq_vec.action[nr]àbh_action(nr)-àbh_base[nr]()
时钟中断:
初始化在start_kernel中,位于调度的初始化之后,因为一旦时钟中断开始生效,则马上开始调度。
sched_init(); time_init();
系统时钟,主要是两个全局变量,一个是struct timeval xtime,代表从历史某一时刻开始的时间绝对值,一般是1970年开始;
另外一个是开机以来的时钟中断的次数,unsigned ling jiffies,而中断的间隔时间,称为一个tick,取决于系统中一个常数,tick一般是20ms
中断初始化time_init函数的主要作用是,将0号向量注册为时间中断irqaction
set_irq(0,&irq0);
而irq0是一个irqaction结构
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0 ,”timer”, NULL, NULL};
ps: 在time.c中irq0被声明为static,说明该变量只能在timer.c中被使用。
第二个参数被置SA_INTERRUPT,所以该中断不能被嵌套,中断服务器程序为timer_interrupt-àdo_timer_interrupt(irq,NULL,regs)àdo_timer
{
(*(unsigned long*)&jiffies)++;
mark_bh(TIMER_BH);
mark_bh(TQUEUE_BH);
}
解释一下,第一句为啥不用jiffies++ ,这样写是因为,编译器会把这种写法翻译成一条原子指令(对内存单元的INC指令)
后面的两个mark_bh,是分别“激活”TIMER_BH,TQUEUE_BH,所谓激活的意思,就是将bh_task_vec[TIMER_BH]挂载到tasklet_hi_vec队列中,同时也置该CPU对应软中断位1:mark_bh(nr)àtasklet_hi_schedule(bh_task_vec+nr)
{
tasklet_hi_vec[cpu].list = bh_task_vec+nr;
cpu_raise_softirq
}àcpu_raise_softirq(cpu,nr)
{
softirq_active(cpu)|=1<
}
中断返回之前,执行与TIMER_BH对应的timer_bh函数,这个是在sched_init中初始化的:
init_bh(TIMER_BH,timer_bh);
timer_bh,就是时间中断的软中断部分要完成的函数实现,也是时钟中断最重要的函数。前面的硬中断,do_timer中完成的功能很少,并且要在关中断的情况下执行,而timer_bh是大多数操作实现的地方。
timer_bh完成的功能,主要是,update_timesà更新wall timer,以及计算cpu负荷的统计信息cal_load
以及,最终的run_timer_list就是定时器执行的函数操作,比如用户定义的到点操作函数,都是在这个函数里完成的。timer_list的结构如下:
struct timer_list{
unsigned long expire;
void (*function)(unsigned long);
}