linux中断线程化

引用网址:

http://edu.codepub.com/2010/0209/20378.phplinux的中断线程化实现

http://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/index.html

Linux内核中断内幕

中断线程化介绍InterruptThreads

在嵌入式领域,业界对Linux实时性的呼声越来越高,对中断进行改造势在必行。在Linux中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。

目前较新的Linux2.6.17还不支持中断线程化。但由IngoMolnar设计并实现的实时补丁,实现了中断线程化。最新的下载地址为:

http://people.redhat.com/~mingo/realtime-preempt/patch-2.6.17-rt9

下面将对中断线程化进行简要分析。

在初始化阶段,中断线程化的中断初始化与常规中断初始化大体上相同,在start_kernel()函数中都调用了trap_init()init_IRQ()两个函数来初始化irq_desc_t结构体不同点主要体现在内核初始化创建init线程时中断线程化的中断在init()函数中还将调用init_hardirqskernel/irq/manage.c(已经打过上文提到的补丁)),来为每一个IRQ创建一个内核线程,最高实时优先级为50,依次类推直到25,因此任何IRQ线程的最低实时优先级为25

void__initinit_hardirqs(void)

{

……

for(i=0;i<NR_IRQS;i++){

irq_desc_t*desc=irq_desc+i;

if(desc->action&&!(desc->status&IRQ_NODELAY))

desc->thread=kthread_create(do_irqd,desc,"IRQ%d",irq);

……

}

}

staticintdo_irqd(void*__desc)

{

……

/*

*Scaleirqthreadprioritiesfromprio50toprio25

*/

param.sched_priority=curr_irq_prio;

if(param.sched_priority>25)

curr_irq_prio=param.sched_priority-1;

……

}

如果某个中断号状态位中的IRQ_NODELAY被置位,那么该中断不能被线程化。

在中断处理阶段,两者之间的异同点主要体现在:两者相同的部分是当发生中断时,CPU将调用do_IRQ()函数来处理相应的中断,do_IRQ()在做了必要的相关处理之后调用__do_IRQ()两者最大的不同点体现在__do_IRQ()函数中,在该函数中,将判断该中断是否已经被线程化(如果中断描述符的状态字段不包含IRQ_NODELAY标志,则说明该中断被线程化了),对于没有线程化的中断,将直接调用handle_IRQ_event()函数来处理。

fastcallnotraceunsignedint__do_IRQ(unsignedintirq,structpt_regs*regs)

{

……

if(redirect_hardirq(desc))

gotoout_no_end;

……

action_ret=handle_IRQ_event(irq,regs,action);

……

}

intredirect_hardirq(structirq_desc*desc)

{

……

if(!hardirq_preemption||(desc->status&IRQ_NODELAY)||!desc->thread)

return0;

……

if(desc->thread&&desc->thread->state!=TASK_RUNNING)

wake_up_process(desc->thread);

……

}

对于已经线程化的情况,调用wake_up_process()函数唤醒中断处理线程,并开始运行,内核线程将调用do_hardirq()来处理相应的中断,该函数将判断是否有中断需要被处理,如果有就调用handle_IRQ_event()来处理。handle_IRQ_event()将直接调用相应的中断处理函数来完成中断处理。

不难看出,不管是线程化还是非线程化的中断,最终都会执行handle_IRQ_event()函数来调用相应的中断处理函数,只是线程化的中断处理函数是在内核线程中执行的

并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。如果某个中断需要被实时处理,它可以像时钟中断那样,用SA_NODELAY标志来声明自己非线程化,例如:

staticstructirqactionirq0={

timer_interrupt,SA_INTERRUPT|SA_NODELAY,CPU_MASK_NONE,"timer",NULL,NULL

};

其中,SA_NODELAYIRQ_NODELAY之间的转换,是在setup_irq()函数中完成的。

linux的中断线程化实现

2.6.25.8内核实现了中断线程化,内核为每一个中断向量建立了一个中断线程,具体就是在结构irq_desc中增加了一个task_struct来代表这个线程

structirq_desc{

irq_flow_handler_thandle_irq;

structirq_chip*chip;

structmsi_desc*msi_desc;

void*handler_data;

void*chip_data;

structirqaction*action;/*IRQactionlist*/

unsignedintstatus;/*IRQstatus*/

unsignedintdepth;/*nestedirqdisables*/

unsignedintwake_depth;/*nestedwakeenables*/

unsignedintirq_count;/*FordetectingbrokenIRQs*/

unsignedintirqs_unhandled;

unsignedlonglast_unhandled;/*Agingtimerforunhandledcount*/

structtask_struct*thread;//中断线程

wait_queue_head_twait_for_handler;

cycles_ttimestamp;

raw_spinlock_tlock;

constchar*name;

}

在中断产生的时候,还是和往常一样进入do_IRQ,这个函数几乎没有什么变化,在do_IRQ中调用了irq_deschandle_irq函数,这个handle_irq是向量相关的,比如有边缘触发等等,这个方式涉及到了硬件规程,故不深入讨论,实际上,每个总线邦定到一个中断向量,而总线的中断方式是总线相关的所以中断向量的方式也就和硬件相关了,这里就以Leveltype为例来说明,Leveltypehandle_irqhandle_level_irq

voidhandle_level_irq(unsignedintirq,structirq_desc*desc)

{

unsignedintcpu=smp_processor_id();

structirqaction*action;

irqreturn_taction_ret;

spin_lock(&desc->lock);

mask_ack_irq(desc,irq);//屏蔽该中断,以防重入

if(unlikely(desc->status&IRQ_INPROGRESS))//如果正在处理则返回

gotoout_unlock;

desc->status&=~(IRQ_REPLAY|IRQ_WAITING);

kstat_cpu(cpu).irqs[irq]++;

action=desc->action;

if(unlikely(!action||(desc->status&IRQ_DISABLED)))//禁用则返回

gotoout_unlock;

desc->status|=IRQ_INPROGRESS;//标记为正在处理

if(redirect_hardirq(desc))//检测是否为线程化中断,若是则唤醒中断线程

gotoout_unlock;

spin_unlock(&desc->lock);

action_ret=handle_IRQ_event(irq,action);//非线程化中断,处理之

if(!noirqdebug)

note_interrupt(irq,desc,action_ret);

spin_lock(&desc->lock);

desc->status&=~IRQ_INPROGRESS;

if(!(desc->status&IRQ_DISABLED)&&desc->chip->unmask)

desc->chip->unmask(irq);

out_unlock:

spin_unlock(&desc->lock);

}

我们看看每个中断向量的中断线程是怎么初始化的,初始化的细节可以带给我们一大部分必要的信息,在实际开发中一定注意这一点,一个好的初始化带来的是将来操作的方便与清晰:

void__initinit_hardirqs(void)

{

inti;

ok_to_create_irq_threads=1;

for(i=0;i<NR_IRQS;i++){//对于每一个中断向量建立一个中断线程

irq_desc_t*desc=irq_desc+i;

if(desc->action&&!(desc->status&IRQ_NODELAY))//有IRQ_NODELAY标志的中断不允许线程化

start_irq_thread(i,desc);//实际建立线程

}

}

staticintstart_irq_thread(intirq,structirq_desc*desc)

{

if(desc->thread||!ok_to_create_irq_threads)

return0;

desc->thread=kthread_create(do_irqd,desc,"IRQ-%d",irq);//建立一个内核线程,赋值给desc->thread

if(!desc->thread){

return-ENOMEM;

}

smp_mb();

wake_up_process(desc->thread);//一切就绪之前即desc->thread被赋值之前可能已经有了中断,故唤醒该中断线程处理之。

return0;

}

staticintdo_irqd(void*__desc)

{

structsched_paramparam={0,};

structirq_desc*desc=__desc;

current->flags|=PF_NOFREEZE|PF_HARDIRQ;

param.sched_priority=MAX_USER_RT_PRIO/2;

sys_sched_setscheduler(current->pid,SCHED_FIFO,¶m);//设置实时优先级

while(!kthread_should_stop()){

local_irq_disable_nort();

do{

set_current_state(TASK_INTERRUPTIBLE);//一定设置这个标志,否则很少有进程可以抢占中断线程,毕竟它是实时线程,如果不设这个标志,即使下面它自己schedule了,那么很大的可能性还是会选中它的

do_hardirq(desc);//处理中断请求

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值