[linux中断]——软中断机制



点击打开链接

一,linux软中断的概念
软中断(softirq)常常表示可延迟函数的所有种类,目前linux上使用的软中断个数是有限的,linux最多注册32个,目前使用了10个,在interrupt.h中定义,中断上下文:表示内核当前正在执行一个中断处理程序或者一个可延迟函数。软中断(即使同一类型的软中断)可以并发运行在多个CPU上,因此软中断是可重入函数必须使用自旋锁保护其数据结构。一个软中断不会去抢占另外一个软中断。

软中断和tasklet的区别
由于软中断必须使用可重入函数,这就导致设计上的复杂度变高,作为设备驱动程序的开发者来说,增加了负担。而如果某种应用并不需要在多个CPU上并行执行,那么软中断其实是没有必要的。因此诞生了弥补以上两个要求的tasklet。它具有以下特性:
a)一种特定类型的tasklet只能运行在一个CPU上,不能并行,只能串行执行。
b)多个不同类型的tasklet可以并行在多个CPU上。
c)软中断是静态分配的,在内核编译好之后,就不能改变。但tasklet就灵活许多,可以在运行时改变(比如添加模块时)。

软中断的实现:
软中断由softirq_action结构体表示

struct softirq_action{
 void (*action)(struct sotfirq_action*)
kernel/softirq.c中定义了一个包含有32个该结构体的数组
static struct softirq_action softirq_vec[NR_SOFTIRQS]

1,注册软中断函数 open_softirq

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
    /* softirq_vec是个struct softirq_action类型的数组 */
    softirq_vec[nr].action = action;
}


2,触发软中断的函数 raise_softirq 参见 kernel/softirq.c文件

void raise_softirq(unsigned int nr)
{
    unsigned long flags;

    local_irq_save(flags);
    raise_softirq_irqoff(nr);
    local_irq_restore(flags);
}


3,执行软中断 do_softirq 参见 kernel/softirq.c文件,如果有待处理的软中断,do_softirq()会循环遍历每一个,调用它们的处理程序。

asmlinkage void do_softirq(void)
{
    __u32 pending;
    unsigned long flags;
    /* 判断是否在中断处理中,如果正在中断处理,就直接返回 */
    if (in_interrupt())
        return;
    /* 保存当前寄存器的值 */
    local_irq_save(flags);
    /* 取得当前已注册软中断的位图 */
    pending = local_softirq_pending();
    /* 循环处理所有已注册的软中断 */
    if (pending)
        __do_softirq();
    /* 恢复寄存器的值到中断处理前 */
    local_irq_restore(flags);
}


4,执行相应的软中断 - 执行自己写的中断处理linux中,执行软中断有专门的内核线程,每个处理器对应一个线程,名称ksoftirqd/n


5,函数__do_softirq()Pending表示的待执行的软中断的位图。当执行软中断的时间大于MAX_SOFTIRQ_RESTART时候会调用某个专门线程来执行,防止watchdog timeout

1.  asmlinkage void __do_softirq(void)

2.  {
3.      struct softirq_action *h;
4.      __u32 pending;
5.      int max_restart = MAX_SOFTIRQ_RESTART; /*不启动ksoftirqd之前,最大的处理softirq的次数,经验值*/
6.      int cpu;
7.   
8.      /*取得当前被挂起的softirq,同时这里也解释了为什么Linux内核最多支持32个softirq,因为pending只有32bit*/
9.      pending = local_softirq_pending(); 
10.     account_system_vtime(current);
11.  
12.     __local_bh_disable((unsigned long)__builtin_return_address(0));
13.     lockdep_softirq_enter();
14.  
15.     cpu = smp_processor_id();
16. restart:
17.     /* Reset the pending bitmask before enabling irqs */
18.     set_softirq_pending(0);/*获取了pending的softirq之后,清空所有pending的softirq的标志*/
19.  
20.     local_irq_enable();
21.  
22.     h = softirq_vec;
23.  
24.     do {
25.         if (pending & 1) { /*从最低位开始,循环右移逐位处理pending的softirq*/
26.             int prev_count = preempt_count();
27.             kstat_incr_softirqs_this_cpu(h - softirq_vec);
28.  
29.             trace_softirq_entry(h, softirq_vec);
30.             h->action(h); /*执行softirq的处理函数*/
31.             trace_softirq_exit(h, softirq_vec);
32.             if (unlikely(prev_count != preempt_count())) {
33.                 printk(KERN_ERR "huh, entered softirq %td %s %p"
34.                  "with preempt_count %08x,"
35.                  " exited with %08x?\n", h - softirq_vec,
36.                  softirq_to_name[h - softirq_vec],
37.                  h->action, prev_count, preempt_count());
38.                 preempt_count() = prev_count;
39.             }
40.  
41.             rcu_bh_qs(cpu);
42.         }
43.         h++;
44.         pending >>= 1;  /*循环右移*/
45.     } while (pending);
46.  
47.     local_irq_disable();
48.  
49.     pending = local_softirq_pending();
50.     if (pending && --max_restart)  /*启动ksoftirqd的阈值*/
51.         goto restart;
52.  
53.     if (pending)  /*启动ksoftirqd去处理softirq,此时说明pending的softirq比较多,比较频繁,上面的处理过程中,又不断有softirq被pending*/
54.         wakeup_softirqd();
55.  
56.     lockdep_softirq_exit();
57.  
58.     account_system_vtime(current);
59.     _local_bh_enable();
60. }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值