linux内核中的软中断的实现
2016-06-04 10:22
323 查看
最近在阅读linux内核源码,把自己的一些理解发上来,一方面看到的朋友可以帮我指正我理解偏差的地方,别一方面也算是做一个简单的总结。
首先调用open_softirq()函数来初始化软件中断处理函数,将软件中断处理函数根据软中断的下标号插入到softirq_vec数组中,实现过程很简单如下:
softirq_vec数据有32个元素,对应的是可以有32个软件中断,但实际上linux只是使用了其中的6个软中断,相应的每一个CPU都会有一个对应的32位的掩码__softirq_pending描述挂起的软中断,每一位对应一个软件中断,__soctirq_penging在irq_cpustat_t中定义,如下:
@include/asm/hardirq.h
其中local_softirq_pending()宏用于选择当前CPU所对应的__softirq_penging掩码。相关的宏如下:
@include/linux/irq_cpustat.h
接着调用raise_softirq()函数来激活软中断,函数如下:
@kernel/softirq.c
@kernel/softirq.craise_softirq_irqoff()函数必须是在禁止中断的情况下执行的,它首先调用__raise_softirq_irqoff()宏激活软件中断,其实也就是设置当前CPU所对应的__softirq_pending所对应的软中断的位,以表示该软中断已激活。如果当前正处于中断或者软中断当中,那么raise_softirq_irqoff执行结束,否则的话就调用wakeup_softirqd()函数激活ksoftirqd/n内核线程来处理软中断。
__rarse_softirq_irqoff()宏在/include/linux/interput.h中定义:
其中local_softirq_pending()宏已经在上面列出来了,我不太明白的是__raise_softirq_irqoff()这个宏为什么要放到一个循环里面,它也只是执行了一次,不过linux既然这样写就一定有它的道理,慢慢再去研究吧。
这样软中断已经激活,其处理函数已经加入到了softirq_sec数组中,并且相应的标志位已经合适地设置,系统会周期性地检查已经挂起的软中断,当在local_softirq_pending()中的某个位检测到挂起的软中断的时候,就会调用do_softirq()函数对软中断进行处理。
do_softirq()函数定义如下:
@ kernel/softirq.h
如果在中断上下文中调用了do_softirq函数或者禁用了软件中断,那么in_interrupt函数返回1,这时候do_softirq()不做任何事情,否则,它会把eflags寄存器IF标志保存在flags中并禁用中断,然后将local_softirq_pending()的值保存在pending变量中,检测pending的值,如果pending不为0,则表示pending的某一位被设置,当前CPU就有对应的挂起的软中断需要进行处理,调用__do_softirq()对挂起的软中断进行处理,处理完成之后再根据flags变量的值恢复eflags寄存器并打开中断。
接下来看__do_softirq()函数:
内核循环地检测每一个pending的位,以处理当前位挂机的软件中断,为了防止内核在执行__do_softirq()的过程中不停地有新的softirq产生,以导致内核无暇顾及其它,__do_softirq()在循环检测的时候设置了外层循环的最大次数为10次,对pending的每一位检测10次之后如果pending的值仍不为0,则表示当前CPU仍有未处理的挂起的软件中断,这时候__do_softirq不再对它进行处理,而是唤醒内核线程ksoftirqd/n对它进行处理。
在函数开始的时候,先将软件中断的位图__softirq_pending保存在pending局部变量中,然后调用set_softirq_pending(0)以清空软中断位图,从而允许新的软中断的到来。接着调用local_irq_enable()来激活软中断。
原文链接地址: http://basiccoder.com/kernel-softirq.html
首先调用open_softirq()函数来初始化软件中断处理函数,将软件中断处理函数根据软中断的下标号插入到softirq_vec数组中,实现过程很简单如下:
void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; }
softirq_vec数据有32个元素,对应的是可以有32个软件中断,但实际上linux只是使用了其中的6个软中断,相应的每一个CPU都会有一个对应的32位的掩码__softirq_pending描述挂起的软中断,每一位对应一个软件中断,__soctirq_penging在irq_cpustat_t中定义,如下:
@include/asm/hardirq.h
typedef struct { unsigned int __softirq_pending; unsigned long idle_timestamp; unsigned int __nmi_count; /* arch dependent */ unsigned int __irq_count; /* arch dependent */ } ____cacheline_aligned irq_cpustat_t;
其中local_softirq_pending()宏用于选择当前CPU所对应的__softirq_penging掩码。相关的宏如下:
@include/linux/irq_cpustat.h
#define local_softirq_pending() \ __IRQ_STAT(smp_processor_id() , __softirq_pending) #define __IRQ_STAT(cpu , member) (irq_stat[cpu].member)
接着调用raise_softirq()函数来激活软中断,函数如下:
@kernel/softirq.c
void raise_softirq(unsigned int nr) { unsigned long flags; /* 将eflags寄存器的IF标志保存到flags,并且禁用了本地CPU上的中断 */ local_irq_save(flags); raise_softirq_irqoff(nr); /* 根据flags变量的值恢复eflags寄存器的IF标志,重新打开本地CPU上的中断 */ local_irq_restore(flags); }
@kernel/softirq.craise_softirq_irqoff()函数必须是在禁止中断的情况下执行的,它首先调用__raise_softirq_irqoff()宏激活软件中断,其实也就是设置当前CPU所对应的__softirq_pending所对应的软中断的位,以表示该软中断已激活。如果当前正处于中断或者软中断当中,那么raise_softirq_irqoff执行结束,否则的话就调用wakeup_softirqd()函数激活ksoftirqd/n内核线程来处理软中断。
inline void raise_softirq_irqoff(unsigned int nr) { __raise_softirq_irqoff(nr); if (!in_interrupt()) wakeup_softirqd(); }
__rarse_softirq_irqoff()宏在/include/linux/interput.h中定义:
#defome __raise_softirq_irqoff(nr) \ do{ or_softirq_pending(1UL << (nr)); } while(0) #define or_softirq_pending(x) (local_softirq_pending() |= (x))
其中local_softirq_pending()宏已经在上面列出来了,我不太明白的是__raise_softirq_irqoff()这个宏为什么要放到一个循环里面,它也只是执行了一次,不过linux既然这样写就一定有它的道理,慢慢再去研究吧。
这样软中断已经激活,其处理函数已经加入到了softirq_sec数组中,并且相应的标志位已经合适地设置,系统会周期性地检查已经挂起的软中断,当在local_softirq_pending()中的某个位检测到挂起的软中断的时候,就会调用do_softirq()函数对软中断进行处理。
do_softirq()函数定义如下:
@ kernel/softirq.h
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); }
如果在中断上下文中调用了do_softirq函数或者禁用了软件中断,那么in_interrupt函数返回1,这时候do_softirq()不做任何事情,否则,它会把eflags寄存器IF标志保存在flags中并禁用中断,然后将local_softirq_pending()的值保存在pending变量中,检测pending的值,如果pending不为0,则表示pending的某一位被设置,当前CPU就有对应的挂起的软中断需要进行处理,调用__do_softirq()对挂起的软中断进行处理,处理完成之后再根据flags变量的值恢复eflags寄存器并打开中断。
接下来看__do_softirq()函数:
asmlinkage void __do_softirq(void) { struct softirq_action *h; __u32 pending; int max_restart = MAX_SOFTIRQ_RESTART; int cpu; pending = local_softirq_pending(); /* 增加preempt_count字段中软中断计数器的值, * 以此来禁用软中断,因为软件中断的可延迟函数 * 一般是在开中断的情况下执行的,这样就可能在__do_softirq 执行 * 的过程中有别一个__do_softirq的实例正在执行 * ,这样就会影响可延迟函数的串行化执行*/ __local_bh_disable((unsigned long)__builtin_return_address(0)); restart: set_softirq_pending(0); local_irq_enable(); h = softirq_vec; do { if (pending & 1) { 9c20 int prev_count = preempt_count(); /* 执行软件中断处理函数 */ h->action(h); if (unlikely(prev_count != preempt_count())) { printk(KERN_ERR "huh, entered softirq %td %p" "with preempt_count %08x," " exited with %08x?\n", h - softirq_vec, h->action, prev_count, preempt_count()); preempt_count() = prev_count; } } h++; pending >>= 1; } while (pending); /* 禁用本地软中断 */ local_irq_disable(); pending = local_softirq_pending(); if (pending && --max_restart) goto restart; if (pending) wakeup_softirqd(); _local_bh_enable(); }
内核循环地检测每一个pending的位,以处理当前位挂机的软件中断,为了防止内核在执行__do_softirq()的过程中不停地有新的softirq产生,以导致内核无暇顾及其它,__do_softirq()在循环检测的时候设置了外层循环的最大次数为10次,对pending的每一位检测10次之后如果pending的值仍不为0,则表示当前CPU仍有未处理的挂起的软件中断,这时候__do_softirq不再对它进行处理,而是唤醒内核线程ksoftirqd/n对它进行处理。
在函数开始的时候,先将软件中断的位图__softirq_pending保存在pending局部变量中,然后调用set_softirq_pending(0)以清空软中断位图,从而允许新的软中断的到来。接着调用local_irq_enable()来激活软中断。
原文链接地址: http://basiccoder.com/kernel-softirq.html
相关文章推荐
- 戴文的Linux内核专题:01 介绍
- 戴文的Linux内核专题:09 配置内核(5)
- 戴文的Linux内核专题:10 配置内核(6)
- 戴文的Linux内核专题:02 源代码
- 戴文的Linux内核专题:03 驱动程序
- 戴文的Linux内核专题:04 安全
- 戴文的Linux内核专题:05 配置内核 (1)
- 戴文的Linux内核专题:07 配置内核 (3)
- 戴文的Linux内核专题:08 配置内核(4)
- 戴文的Linux内核专题:11 配置内核(7)
- 戴文的Linux内核专题:13 配置内核(9)
- 戴文的Linux内核专题:14 配置内核 (10)
- 戴文的Linux内核专题:15 配置内核 (11)
- 戴文的Linux内核专题:17 配置内核 (13)
- 戴文的Linux内核专题:18 配置内核 (14)
- 戴文的Linux内核专题:19 配置内核 (15)
- 戴文的Linux内核专题:20 配置内核 (16)
- 戴文的Linux内核专题:21 配置内核 (17)