linux添加软中断,Linux下软中断(一)

unsigned int __syscall_count;

unsigned int __nmi_count; /* arch dependent

*/

} ____cacheline_aligned irq_cpustat_t;

结构中每一个成员都是一个32位的无符号整数。其中__softirq_active和

__softirq_mask就是用于触发和控制软中断的成员变量。

①__softirq_active变量:32位的无符号整数,表示软中断向量0~31的状态。如果bit

[i](0≤i≤31)为1,则表示软中断向量i在某个CPU上已经被触发而处于active状

态;为0表示处于非活跃状态。

②__softirq_mask变量:32位的无符号整数,软中断向量的屏蔽掩码。如果bit[i](0

≤i≤31)为1,则表示使能(enable)软中断向量i,为0表示该软中断向量被禁止

(disabled)。

根据系统中当前的CPU个数(由宏NR_CPUS表示),Linux在kernel/softirq.c文件中为每

个CPU都定义了它自己的中断统计信息结构,如下所示:

/* No separate irq_stat for s390, it is part of PSA */

#if !defined(CONFIG_ARCH_S390)

irq_cpustat_t irq_stat[NR_CPUS];

#endif /* CONFIG_ARCH_S390 */

这样,每个CPU都只操作它自己的中断统计信息结构。假设有一个编号为id的CPU,那么

它只能操作它自己的中断统计信息结构irq_stat[id](0≤id≤NR_CPUS-1),从而使

各CPU之间互不影响。这个数组在include/linux/irq_cpustat.h头文件中也作了原型声

明。

l 触发软中断请求的操作函数

函数__cpu_raise_softirq()用于在编号为cpu的处理器上触发软中断向量nr。它通过将

相应的__softirq_active成员变量中的相应位设置为1来实现软中断触发。如下所示

(include/linux/interrupt.h):

static inline void __cpu_raise_softirq(int cpu, int nr)

{

softirq_active(cpu) |= (1

为了保证“原子”性地完成软中断的触发过程,Linux在interrupt.h头文件中对上述内

联函数又作了高层封装,也即函数raise_softirq()。该函数向下通过调用

__cpu_raise_softirq()函数来实现软中断的触发,但在调用该函数之前,它先通过

local_irq_save()函数来关闭当前CPU的中断并保存标志寄存器的内容,如下所示:

/* I do not want to use atomic variables now, so that cli/sti

*/

static inline void raise_softirq(int nr)

{

unsigned long flags;

local_irq_save(flags);

__cpu_raise_softirq(smp_processor_id(), nr);

local_irq_restore(flags);

}

在软中断向量0~31中,Linux内核仅仅使用了软中断向量0~3,其余被留待系统以后扩

展。Linux在头文件include/linux/interrupt.h中对软中断向量0~3进行了预定义:

/* PLEASE, avoid to allocate new softirqs, if you need not _really_

high

frequency threaded job scheduling. For almost all the

purposes

tasklets are more than enough. F.e. all serial device BHs et

al. should be converted to tasklets, not to softirqs.

*/

enum

{

HI_SOFTIRQ=0,

NET_TX_SOFTIRQ,

NET_RX_SOFTIRQ,

TASKLET_SOFTIRQ

};

其中,软中断向量0(即HI_SOFTIRQ)用于实现高优先级的软中断,如:高优先级的

tasklet(将在后面详细描述)。软中断向量1和2则分别用于网络数据的发送与接收。软

中断向量3(即TASKLET_SOFTIRQ)则用于实现诸如tasklet这样的一般性软中断。关于

tasklet我们将在后面详细描述。NOTE!Linix内核并不鼓励一般用户扩展使用剩余的软

中断向量,因为它认为其预定义的软中断向量HI_SOFTIRQ和TASKLET_SOFTIRQ已经足够应

付绝大多数应用。

函数softirq_init()完成softirq机制的初始化。该函数由内核启动例程start_kernel()

所调用。函数源码如下所示(kernel/softirq.c):

void __init softirq_init()

{

int i;

for (i=0; i<32; i++)

tasklet_init(bh_task_vec+i, bh_action, i);

open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);

open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);

}

初始化的过程如下:

(1)先用一个for循环来初始化用于实现BH机制的bh_task_vec[32]数组。这一点我们

将在后面详细解释。

(2)调用open_softirq()函数开启使用软中断向量TASKLET_SOFTIRQ和HI_SOFTIRQ,并

将它们的软中断服务函数指针分别指向tasklet_action()函数和tasklet_hi_action()

函数。函数open_softirq()的主要作用是初始化设置软中断请求描述符softirq_vec

[nr]。

函数open_softirq()用于开启一个指定的软中断向量nr,也即适当地初始化软中断向量

nr所对应的软中断描述符softirq_vec[nr]。它主要做两件事情:(1)初始化设置软

中断向量nr所对应的软中断描述符softirq_vec[nr]。(2)将所有CPU的软中断屏蔽掩

码变量__softirq_mask中的对应位设置为1,以使能该软中断向量。该函数的源码如下所

示(kernel/softirq.c):

void open_softirq(int nr, void (*action)(struct softirq_action*),

void

*data)

{

unsigned long flags;

int i;

spin_lock_irqsave(&softirq_mask_lock, flags);

softirq_vec[nr].data = data;

softirq_vec[nr].action = action;

for (i=0; isoftirq_mask(i) |=

(1

flags);

}

函数do_softirq()负责执行数组softirq_vec[32]中设置的软中断服务函数。每个CPU

都是通过执行这个函数来执行软中断服务的。由于同一个CPU上的软中断服务例程不允许

嵌套,因此,do_softirq()函数一开始就检查当前CPU是否已经正出在中断服务中,如果

是则do_softirq()函数立即返回。举个例子,假设CPU0正在执行do_softirq()函数,执

行过程产生了一个高优先级的硬件中断,于是CPU0转去执行这个高优先级中断所对应的

中断服务程序。总所周知,所有的中断服务程序最后都要跳转到do_IRQ()函数并由它来

依次执行中断服务队列中的ISR,这里我们假定这个高优先级中断的ISR请求触发了一次

软中断,于是do_IRQ()函数在退出之前看到有软中断请求,从而调用do_softirq()函数

来服务软中断请求。因此,CPU0再次进入do_softirq()函数(也即do_softirq()函数在

CPU0上被重入了)。但是在这一次进入do_softirq()函数时,它马上发现CPU0此前已经

处在中断服务状态中了,因此这一次do_softirq()函数立即返回。于是,CPU0回到该开

始时的do_softirq()函数继续执行,并为高优先级中断的ISR所触发的软中断请求补上一

次服务。从这里可以看出,do_softirq()函数在同一个CPU上的执行是串行的。

函数源码如下(kernel/softirq.c):

asmlinkage void do_softirq()

{

int cpu = smp_processor_id();

__u32 active, mask;

if (in_interrupt())

return;

local_bh_disable();

local_irq_disable();

mask = softirq_mask(cpu);

active = softirq_active(cpu) & mask;

if (active) {

struct softirq_action *h;

restart:

/* Reset active bitmask before enabling irqs */

softirq_active(cpu) &= ~active;

local_irq_enable();

h = softirq_vec;

mask &= ~active;

do {

if (active & 1)

h->action(h);

h++;

active >>= 1;

} while (active);

local_irq_disable();

active = softirq_active(cpu);

if ((active &= mask) != 0)

goto retry;

}

local_bh_enable();

/* Leave with locally disabled hard irqs. It is critical to

close

* window for infinite recursion, while we help local bh

count,

* it protected us. Now we are defenceless.

*/

return;

retry:

goto restart;

}

结合上述源码,我们可以看出软中断服务的执行过程如下:

(1)调用宏in_interrupt()来检测当前CPU此次是否已经处于中断服务中。该宏定义在

hardirq.h,请参见5.7节。

(2)调用local_bh_disable()宏将当前CPU的中断统计信息结构中的__local_bh_count

成员变量加1,表示当前CPU已经处在软中断服务状态。

(3)由于接下来要读写当前CPU的中断统计信息结构中的__softirq_active变量和

__softirq_mask变量,因此为了保证这一个操作过程的原子性,先用local_irq_disable

()宏(实际上就是cli指令)关闭当前CPU的中断。

(4)然后,读当前CPU的__softirq_active变量值和__softirq_mask变量值。当某个软

中断向量被触发时(即__softirq_active变量中的相应位被置1),只有__softirq_mask

变量中的相应位也为1时,它的软中断服务函数才能得到执行。因此,需要将

__softirq_active变量和__softirq_mask变量作一次“与”逻辑操作。

(5)如果active变量非0,说明需要执行软中断服务函数。因此:①先将当前CPU的

__softirq_active中的相应位清零,然后用local_irq_enable()宏(实际上就是sti指

令)打开当前CPU的中断。②将局部变量mask中的相应位清零,其目的是:让do_softirq

()函数的这一次执行不对同一个软中断向量上的再次软中断请求进行服务,而是将它留

待下一次do_softirq()执行时去服务,从而使do_sottirq()函数避免陷入无休止的软中

断服务中。③用一个do{}while循环来根据active的值去执行相应的软中断服务函数。④

由于接下来又要检测当前CPU的__softirq_active变量,因此再一次调用

local_irq_disable()宏关闭当前CPU的中断。⑤读取当前CPU的__softirq_active变量的

值,并将它与局部变量mask进行与操作,以看看是否又有其他软中断服务被触发了(比

如前面所说的那种情形)。如果有的话,那就跳转到entry程序段(实际上是跳转到

restart程序段)重新执行软中断服务。如果没有的话,那么此次软中断服务过程就宣告

结束。

(6)最后,通过local_bh_enable()宏将当前CPU的__local_bh_count变量值减1,表示

当前CPU已经离开软中断服务状态。宏local_bh_enable()也定义在include/asm-

i386/softirq.h头文件中。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值