linux中断处理子系统小结

前面的博文中, 大致分散介绍了一些中断相关的东西, 但是对软中断的部分没有仔细介绍, 在这里统一总结一下.

中断上半部的处理,汇编到request_irq的handle之间的过程.
http://blog.csdn.net/jackjones_008/article/details/42387241
MIPS平台的一点记录
http://blog.csdn.net/jackjones_008/article/details/41945909

tasklet/workqueue的介绍在下面这篇博文中有比较详细的介绍.
http://blog.csdn.net/jackjones_008/article/details/42295411


中断的处理, 首先当然是硬件中断发生后汇编部分的处理, 然后在hardirq处理完之后, 在函数irq_exit中, 如果发现当前没有hardirq而且softirq没有被禁止,

也没有pending的本地softirq, 则会显示的调用do_softirq 去处理softirq. 在处理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);
}

在__do_softirq 里面, 在得到pending的softirq后, 又会打开硬件中断. 然后会去依次处理pending 的softirq.
这个依次, 其实就是根据enum中的定义从 HI_SOFTIRQ 执行到 RCU_SOFTIRQ.
再多啰嗦一下, 为什么会有pending的softirq呢, 因为代码有些地方去调用了 __raise_softirq_irqoff .
我们还可以注意到, __do_softirq 里面定义了一个max_restart, 这个值是10, 是系统的一个折中, 处理软中断, 但是不能过度的影响其他进程的执行.
完成不了的, 可以交给 ksoftirqd 这个内核thread去做.
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));
	trace_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) {
			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;
			}

			rcu_bh_qsctr_inc(cpu);
		}
		h++;
		pending >>= 1;
	} while (pending);

	local_irq_disable();

	pending = local_softirq_pending();
	if (pending && --max_restart)
		goto restart;

	if (pending)
		wakeup_softirqd();

	trace_softirq_exit();

	account_system_vtime(current);
	_local_bh_enable();
}

enum
{
	HI_SOFTIRQ=0,
	TIMER_SOFTIRQ,
	NET_TX_SOFTIRQ,
	NET_RX_SOFTIRQ,
	BLOCK_SOFTIRQ,
	TASKLET_SOFTIRQ,
	SCHED_SOFTIRQ,
	HRTIMER_SOFTIRQ,
	RCU_SOFTIRQ,	/* Preferable RCU should always be the last softirq */

	NR_SOFTIRQS
};

接上, restart 中处理完后, 还有pending的softirq的话, 就交给内核进程 ksoftirqd 去做. 这个内核进程也会显式的调用 do_softirq 去处理.

static int ksoftirqd(void * __bind_cpu)
{
	set_current_state(TASK_INTERRUPTIBLE);

	while (!kthread_should_stop()) {
		preempt_disable();
		if (!local_softirq_pending()) {
			preempt_enable_no_resched();
			schedule();
			preempt_disable();
		}

		__set_current_state(TASK_RUNNING);

		while (local_softirq_pending()) {
			/* Preempt disable stops cpu going offline.
			   If already offline, we'll be on wrong CPU:
			   don't process */
			if (cpu_is_offline((long)__bind_cpu))
				goto wait_to_die;
			do_softirq();
			preempt_enable_no_resched();
			cond_resched();
			preempt_disable();
			rcu_qsctr_inc((long)__bind_cpu);
		}
		preempt_enable();
		set_current_state(TASK_INTERRUPTIBLE);
	}
	__set_current_state(TASK_RUNNING);
	return 0;

wait_to_die:
	preempt_enable();
	/* Wait for kthread_stop */
	set_current_state(TASK_INTERRUPTIBLE);
	while (!kthread_should_stop()) {
		schedule();
		set_current_state(TASK_INTERRUPTIBLE);
	}
	__set_current_state(TASK_RUNNING);
	return 0;
}

这时, 又不可避免的回到了中断上半部和下半部的话题, 上半部我们可以理解为一直执行到 request_irq 中注册的那个 handle 为止. 而softirq 开始的处理都是属于下半部的处理.
在下半部的处理中, 根据睡眠与否可以使用tasklet和workqueue的机制.
其实tasklet也是属于softirq的一种, 而且不可睡眠. workqueue的机制则是交给了内核thread去执行, 可以允许睡眠.


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。