linux软中断

 在由内核执行的几个任务之间有些不是紧急的,在必要情况下他们可以延迟一段时间。一个中断处理程序的几个中断服务例程之间是串行执行的,并且通常在一个中断的处理程序结束前,不应该再次出现这个中断。相反,可延迟中断可以在开中断的情况下执行。

linux中所谓的可延迟函数,包括软中断和tasklet以及通过中作队列执行的函数(这个以后说),软中断的分配是静态的(即值编译时定义),而tasklet的分配和初始化可以在运行时进行。

软中断

软中断所使用的数据结构定义为

static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

其中softirq_action类型为一个函数指针,从这里也可以看出,软中断的个数是有限的有NR_SOFTIRQS个,具体的定义如下:

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
};

可以看出,一共10个软中断。

struct softirq_action
{
	void	(*action)(struct softirq_action *);
};

软中断的初始化

/*初始化软中断*/
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
	softirq_vec[nr].action = action;
}

上面函数中,参数nr为softirq_vec[]数组的下标,初始化就是初始化softirq_vec[]数组内容。

初始化了软中断后,要执行,接下来要做的是激活软中断,运用下面函数

/*激活软中断*/
void raise_softirq(unsigned int nr)
{
	unsigned long flags;
	/*保存eflags寄存器IF标志的状态值
	并禁用本地CPU上得中断*/
	local_irq_save(flags);
	
	raise_softirq_irqoff(nr);
	local_irq_restore(flags);
}

具体的激活工作由raise_softirq_irqoff函数实现

/*
 * This function must run with irqs disabled!
 */
inline void raise_softirq_irqoff(unsigned int nr)
{	
	/*把软中断标记为挂起*/
	__raise_softirq_irqoff(nr);

	/*
	 * If we're in an interrupt or softirq, we're done
	 * (this also catches softirq-disabled code). We will
	 * actually run the softirq once we return from
	 * the irq or softirq.
	 *
	 * Otherwise we wake up ksoftirqd to make sure we
	 * schedule the softirq soon.
	 */
	if (!in_interrupt())
		wakeup_softirqd();/*唤醒本地的内核线程*/
}

守护线程softirqd就是对软中断的处理

static int ksoftirqd(void * __bind_cpu)
{
	/*设置进程状态为可中断*/
	set_current_state(TASK_INTERRUPTIBLE);

	while (!kthread_should_stop()) {/*不应该马上返回*/
		preempt_disable();
		/*实现软中断中一个关键数据结构是每个
		CPU都有的32位掩码(描述挂起的软中断),
		他存放在irq_cpustat_t数据结构的__softirq_pending
		字段中。为了获取或设置位掩码的值,
		内核使用宏local_softirq_pending,他选择cpu的
		软中断为掩码*/
		if (!local_softirq_pending()) {/*位掩码为0,标示没有软中断*/
			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_sched_qs((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;
}

下面是软中断的执行

/*如果在这样的一个检查点(local_softirq_pending()不为0)
检测到挂起的软中断,内核调用下面函数处理*/
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函数实现

/*读取本地CPU的软中断掩码并执行与每个设置位
相关的可延迟函数,__do_softirq只做固定次数的循环
然后就返回。如果还有其余挂起的软中断,那么
内核线程ksofirqd将会在预期的时间内处理他们*/
asmlinkage void __do_softirq(void)
{
	struct softirq_action *h;
	__u32 pending;
	/*把循环计数器的值初始化为10*/
	int max_restart = MAX_SOFTIRQ_RESTART;
	int cpu;
	/*把本地CPU(被local_softirq_pending选中的)软件中断的
	位掩码复制到局部变量pending中*/
	pending = local_softirq_pending();
	account_system_vtime(current);
	/*增加软中断计数器的值*/
	__local_bh_disable((unsigned long)__builtin_return_address(0));
	lockdep_softirq_enter();

	cpu = smp_processor_id();
restart:
	/* Reset the pending bitmask before enabling irqs */
	set_softirq_pending(0);/*清除本地CPU的软中断位图,
	以便可以激活新的软中断*/

	/*激活本地中断*/
	local_irq_enable();

	h = softirq_vec;

	do {/*根据pending每一位的的设置,执行对应的软中断
		处理函数*/
		if (pending & 1) {
			int prev_count = preempt_count();
			kstat_incr_softirqs_this_cpu(h - softirq_vec);

			trace_softirq_entry(h, softirq_vec);
			h->action(h);/*执行注册的具体的软中断函数*/
			trace_softirq_exit(h, softirq_vec);
			if (unlikely(prev_count != preempt_count())) {
				printk(KERN_ERR "huh, entered softirq %td %s %p"
				       "with preempt_count %08x,"
				       " exited with %08x?\n", h - softirq_vec,
				       softirq_to_name[h - softirq_vec],
				       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)/*如果还有挂起的软中断,唤醒内核线程
			来处理本地CPU的软中断*/
		wakeup_softirqd();

	lockdep_softirq_exit();

	account_system_vtime(current);
	_local_bh_enable();/*软中断计数器-1,因而重新激活可延迟函数*/
}

到此,linux内核软中断的大致执行和实现基本上分析完了,中间有很多地方没有注释的,主要是考虑到需要别的实现机制以及有的比较易懂。能够自己看懂。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值