为什么需要软中断 引入软中断的目的是为了中断要尽快返回。 把一些不紧急的工作放到下半部里执行。 软中断是下半部的一种实现方式。其他的还有tasklet和workqueue。
软中断特点 软中断特点是可以同时运行在CPU上,而tasklet则是整个系统中只有一个。 软中断和tasklet都不允许睡眠,因此必须避免执行引发睡眠的操作。 workqueue则允许睡眠。
软中断原理 原理 每个cpu有一标志位,硬中断产生时候就将这个标志位置1, 之后硬中断就退出。 CPU调度后,执行软中断,以网卡收报为例。软中断将对应的包处理完成, 然后清除相应的标志位。
潜在的问题: 在cpuA上触发了硬中断,那么就会在cpuA执行软中断。 如果一个网卡接受到大量的数据包,那么就会导致该CPU利用率非常高, 而其他的cpux却相对比较空闲。
应对方法 1. 软件实现
goolge patch:将收到的包按流hash,然后分配到不同的cpuB的软中断队列里,并激发cpuB的软中断。
2. 硬件实现
高端的网卡: 如ixgbe 网卡, xlr/xlp的nae等专用平台:网卡(或nae)本身通过参数设置,可以让不同的数据包分别在不同的cpu上触发硬中断上。从而达到将网络包交给到个cpu处理,避免单个cpu过于繁忙。
软中断在内核的实现 软中断的数据结构: ###软中断的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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, NR_SOFTIRQS };
###每个软中断有一个对应的处理函数。
1 2 3 4 static struct softirq_action softirq_vec [NR_SOFTIRQS ] __cacheline_aligned_in_smp ; struct softirq_action { void (*action)(struct softirq_action *); };
percpu的变量 local_softirq_pending()
, 其每一位对应一个软中断类型。 X86 和mips存储方式略有不同。
X86
In the file: include/linux/interrupt.h
1 2 3 4 5 6 7 8 9 10 7 typedef struct { 8 unsigned int __softirq_pending; 9 unsigned int __nmi_count; 10 unsigned int irq0_irqs; ... 30 } ____cacheline_aligned irq_cpustat_t ; ... 32 DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t , irq_stat); ... 41 #define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)
mips
1 #define local_softirq_pending() (local_cpu_data->softirq_pending)
In the file: include/linux/interrupt.h
1 2 3 4 #ifndef __ARCH_SET_SOFTIRQ_PENDING <=== only defined for X86. #define set_softirq_pending(x) (local_softirq_pending() = (x)) #define or_softirq_pending(x) (local_softirq_pending() |= (x)) #endif
include/linux/irq_cpustat.h
1 2 3 4 5 6 7 8 #define local_softirq_pending() \ __IRQ_STAT(smp_processor_id(), __softirq_pending) 19 #ifndef __ARCH_IRQ_STAT 20 extern irq_cpustat_t irq_stat[]; 21 #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member) 22 #endif
kernel/softirq.c
1 2 3 4 50 #ifndef __ARCH_IRQ_STAT 51 irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;52 EXPORT_SYMBOL(irq_stat);53 #endif
softirq init: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 719 void __init softirq_init (void ) 720 { 721 int cpu;722 723 for_each_possible_cpu(cpu) {724 int i;725 726 per_cpu(tasklet_vec, cpu).tail =727 &per_cpu(tasklet_vec, cpu).head;728 per_cpu(tasklet_hi_vec, cpu).tail =729 &per_cpu(tasklet_hi_vec, cpu).head;730 for (i = 0 ; i < NR_SOFTIRQS; i++)731 INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));732 }733 734 register_hotcpu_notifier(&remote_softirq_cpu_notifier);735 736 open_softirq(TASKLET_SOFTIRQ, tasklet_action);737 open_softirq(HI_SOFTIRQ, tasklet_hi_action);738 }
1 2 3 4 388 void open_softirq (int nr, void (*action)(struct softirq_action *)) 389 { 390 softirq_vec[nr].action = action;391 }
1 2 3 4 5 6 7 8 379 void raise_softirq(unsigned int nr) 380 { 381 unsigned long flags; 382 383 local_irq_save(flags); 384 raise_softirq_irqoff(nr); 385 local_irq_restore(flags); 386 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 362 inline void raise_softirq_irqoff (unsigned int nr) 363 { 364 __raise_softirq_irqoff(nr);365 366 375 if (!in_interrupt())376 wakeup_softirqd();377 }