linux中断底半部之 softirq 原理与代码分析

Linuxsoftirq

一、软中断产生的原因

软中断:用于有效的实现内核的延期操作,也是底半部机制tasklet的基础

二、数据结构

1)softirq_action

该结构是软中断的核心数据结构,代表软中断处理函数。

structsoftirq_action

{

void (*action)(structsoftirq_action*);

};

2)下面是软中断的类型,目前支持10个软中断类型。

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, /*PreferableRCUshouldalwaysbethelastsoftirq*/

NR_SOFTIRQS

};

3)softirq_vec

该数组存储了已注册的软中断类型对应的处理函数,该数组非常重要

staticstructsoftirq_actionsoftirq_vec[NR_SOFTIRQS]__cacheline_aligned_in_smp;

三、软中断的注册

要在内核中使用一个软中断,首先要进行注册,所谓注册,其实就是在数组softirq_vec中增加相应软中断的处理函数,调用函数定义如下:

/*

注册一个软中断,即在软中断向量数据中,增加软中断number为nr的处理函数

*/

voidopen_softirq(intnr,void(*action)(structsoftirq_action*))

{

softirq_vec[nr].action=action;

}

四、启动软中断

如果要启动某个软中断,则需要置位irq_stat[cpu].__softirq_pending中的相应位,而后续的处理工作则由do_softirq处理。

而设置__softirq_pending的操作则由函数__raise_softirq_irqoff来实现。

可以通过以下两种方法启动软中断:

1)在中断上下文中,通过调用函数raise_softirq,置位irq_stat[cpu].__softirq_pending中的相应软中断位,则会在中断结束后在函数irq_exit中调用invoke_softirq,实现软中断处理

2)在非中断上下文中,通过调用raise_softirq_irqoff,置位irq_stat[cpu].__softirq_pending中的相应软中断位,并唤醒软中断守护进程,通过软中断守护进程实现软中断的处理

3)在__do_softirq中,当该函数执行完时还有未决的软中断,则唤醒软中断守护进程,由软中断守护进程继续处理未决的软中断

以上3种方法中,不管是通过调用函数invoke_softirq,还是通过软中断守护进程来处理软中断,最终都会调用函数do_softirq、__do_softirq。

do_softirq主要完成以下工作

1)首先判断当前是否在中断上下文中,若是则直接返回

2)关闭irq,读取当前cpu的irq_stat[cpu].__softirq_pending

3)判断__softirq_pending的值是否为0,若不为0则说明有软中断待处理,则调用__do_softirq

asmlinkagevoiddo_softirq(void)

{

__u32pending;

unsignedlongflags;

if(in_interrupt())

return;

local_irq_save(flags);

pending=local_softirq_pending();

if(pending)

__do_softirq();

local_irq_restore(flags);

}

__do_softirq的定义如下:

其主要完成的工作有:

1)获取irq_stat[cpu].__softirq_pending的值

2)重置irq_stat[cpu].__softirq_pending的值为0,并开启软中断

3)获取软中断向量数据softirq_vec

4)在一个while循环中,对于每一个未处理的软中断,执行softirq_vec中相对应的action处理函数

5)关闭中断,重新读取irq_stat[cpu].__softirq_pending的值,若该值不为0则

6)在重复执行的次数没有超过MAX_SOFTIRQ_RESTART,且irq_stat[cpu].__softirq_pending的值不为0时,重新执行上述2、3、4、5的操作

7)若已超过MAX_SOFTIRQ_RESTART,则调用wakeup_softirqd,唤醒软中断守护进程,由软中断守护进程继续处理

#defineMAX_SOFTIRQ_RESTART10

asmlinkagevoid__do_softirq(void)

{

structsoftirq_action*h;

__u32pending;

intmax_restart=MAX_SOFTIRQ_RESTART;

intcpu;

pending=local_softirq_pending();

account_system_vtime(current);

__local_bh_disable((unsignedlong)__builtin_return_address(0));

lockdep_softirq_enter();

cpu=smp_processor_id();

restart:

/*Resetthependingbitmaskbeforeenablingirqs*/

set_softirq_pending(0);

local_irq_enable();

h=softirq_vec;

do{

if(pending&1){

intprev_count=preempt_count();

kstat_incr_softirqs_this_cpu(h-softirq_vec);

trace_softirq_entry(h,softirq_vec);

h->action(h);

trace_softirq_exit(h,softirq_vec);

if(unlikely(prev_count!=preempt_count())){

printk(KERN_ERR"huh,enteredsoftirq%td%s%p"

"withpreempt_count%08x,"

"exitedwith%08x?\n",h-softirq_vec,

softirq_to_name[h-softirq_vec],

h->action,prev_count,preempt_count());

preempt_count()=prev_count;

}

rcu_bh_qs(cpu);

}

h++;

pending>>=1;

}while(pending);

local_irq_disable();

pending=local_softirq_pending();

if(pending&&–max_restart)

gotorestart;

if(pending)

wakeup_softirqd();

lockdep_softirq_exit();

account_system_vtime(current);

_local_bh_enable();

}

疑问:在SMP下,多个CPU可以同时执行同一个软中断吗?

解答:因为每一个CPU都有一个__softirq_pending变量,那么在同一时刻多个CPU的__softirq_pending的某个相同位可能都为1,这样的话,就可能出现在同一时刻多个CPU同时执行同一个软中断。

软中断的应用:

在内核代码中,使用软中断的有:在接收和发送网络数据包时,会借助软中断实现;另一个中断底半部处理机制tasklet也使用了软中断,tasklet即使用软中断作为基础,然后再实现tasklet。在我们编写我们自己的驱动代码时,个人建议首选使用tasklet,而不要使用softirq。

出门走好路,出口说好话,出手做好事。

linux中断底半部之 softirq 原理与代码分析

相关文章:

你感兴趣的文章:

标签云: