小总结:softirq会被中断抢占吗?会被其他的softirq抢占吗?

12 篇文章 0 订阅

软中断主要由do_softirq来执行,看看它的源代码如下:

asmlinkage void do_softirq(void)
{
	__u32 pending;
	unsigned long flags;

    // this is the reason why softirq won't preempt another softirq
    // in_interrupt() contains context of an interrupt (both top and bottom halves)
    // by Tom Xue
	if (in_interrupt())  
        return;
        
	local_irq_save(flags);
    printk("tomxue: flags = %x\n", flags);

	pending = local_softirq_pending();
    printk("tomxue: pending = %x\n", pending);

	if (pending)
		__do_softirq();
     
	local_irq_restore(flags);
}


如果进入__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();
	account_system_vtime(current);

        // 我猜下面两行是禁止了本地的软中断,但在其他处理器上仍可以执行(同一或者其他)软中断
	__local_bh_disable((unsigned long)__builtin_return_address(0),
				SOFTIRQ_OFFSET);
	lockdep_softirq_enter();

	cpu = smp_processor_id();
restart:
	/* Reset the pending bitmask before enabling irqs */
	set_softirq_pending(0);

        // 各个软中断执行时,允许响应本地中断
	local_irq_enable();

	h = softirq_vec;

	do {
		if (pending & 1) {
			unsigned int vec_nr = h - softirq_vec;
			int prev_count = preempt_count();

			kstat_incr_softirqs_this_cpu(vec_nr);

			trace_softirq_entry(vec_nr);
			h->action(h);
			trace_softirq_exit(vec_nr);
			if (unlikely(prev_count != preempt_count())) {
				printk(KERN_ERR "huh, entered softirq %u %s %p"
				       "with preempt_count %08x,"
				       " exited with %08x?\n", vec_nr,
				       softirq_to_name[vec_nr], 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)
		goto restart;

	if (pending)
		wakeup_softirqd();

	lockdep_softirq_exit();

	account_system_vtime(current);
	__local_bh_enable(SOFTIRQ_OFFSET);
}

可见,如果软中断在一开始时,检测到此时正处于中断上下文中( in_interrupt() ),即有别的中断处理程序(top half)或者下半部在运行的话,那么就会立刻返回。

软中断执行过程中,允许响应中断,且当前处理器上的软中断被禁止,但是其他的处理器上仍可以运行别的甚至相同的软中断。

这些别扭的、拗口的规则其实通过查看源代码,很容易看到它们具体是怎么做的。

我的理解是:

  • 中断一般是耽误不得的,只要发生,就立刻去处理,即使是软中断也要被打断,反正中断处理会压栈出栈它能返回嘛。
  • 中断处理时(top half),屏蔽本地中断即本处理器上的中断,是为了防止中断嵌套吧,这样会增加问题的复杂性,同时压栈会消耗内存。反正中断线上的状态会维持住,这是硬件保证的,直到你处理了它,再清除这个状态。这样,top half是串行执行的,不用嵌套,很清爽的设计。且top half执行得快,所以没啥问题。
  • 软中断在本地处理器上可重入?不,这主要是为了降低操作的复杂性。事实上,既然软中断处理过程中开了中断,就会有新的软中断等候执行。但是在当前软中断还在执行过程中,那些新增的软中断应该会被调度到其他处理器上执行吧(我猜的,具体怎么做的,咱再研究疑问)。直到当前软中断执行完了,会再次查看本地软中断pending状态,有pending的软中断,咱就继续执行之。
    	pending = local_softirq_pending();
    	if (pending && --max_restart)
    		goto restart;

好吧,我想说的是,kernel太复杂了,不能事无巨细,只能连蒙带猜去理解,用杨振宁的话说,这叫渗透性学习:确定无疑的,那就是捡到了;模糊不确定的,也已经慢慢渗透进去了,渐渐会越来越清晰的


再把目光放在pending这个变量身上,它是一个32位数(__u32,这应该是出于可移植性考虑而定义的数据类型),然后我们看到里面的do...while()循环用到了它,在循环里不断地做右移操作。这样,我就明白了为什么Robert Love在他的LKD3中说有32个软中断,我想他是说这个可以有。但是Linux中应该目前只用到了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,    /* Preferable RCU should always be the last softirq */

	NR_SOFTIRQS
};

而从__do_softirq()中,我们看到了
pending = local_softirq_pending();
追踪下去,

  /* arch independent irq_stat fields */
#define local_softirq_pending() \
	__IRQ_STAT(smp_processor_id(), __softirq_pending)

继续追踪,

#ifndef __ARCH_IRQ_STAT
extern irq_cpustat_t irq_stat[];		/* defined in asm/hardirq.h */
#define __IRQ_STAT(cpu, member)	(irq_stat[cpu].member)
#endif

继续追踪,

#ifndef __ARCH_IRQ_STAT
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
EXPORT_SYMBOL(irq_stat);
#endif

由此,得出pending应该是这样的:

pending = irq_stat[smp_processor_id()].__softirq_pending

而根据下面的定义,可知irq_stat是个数组,数组的大小即CPU的个数,数组里的每个元素是一个结构体(这个叫per CPU变量吧),而结构体内是一个无符号的整数...

typedef struct {
	unsigned int __softirq_pending;
} ____cacheline_aligned irq_cpustat_t;


不过这个__softirq_pending我暂时没找到它是在哪里赋值的,做个记号吧疑问



如上分析,估计有很多问题,还请各位多多指教!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值