Linux内核软中断及tasklet处理过程

首先要说的是,tasklet也是在软中断处理过程中完成的。

表示一个软中断的结构如下,可以看出,里面是一个函数指针,顾函数名而知其意,就是软中断时的行为,也就是软中断时要做的是什么,由一个函数指针来自由定义。当然,内核已经帮我们定义了一些。

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

使用软中断前要先注册,这和中断处理有点类似,为什么要注册呢?因为在中断时要完成的动作需要我们来定义,比如你要处理网卡接受的数据,内核并不知道要这样做,你要注册一下,让内核清楚这个软中断具体要做些什么。(注:最多可以注册32个软中断)

那么如何注册呢?

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
	softirq_vec[nr].action = action;
}

open_softirq函数用于注册,可以看出此函数无他,就是把一个数组里的第nr项的函数指针赋值为你所需要的那个函数指针,就此,注册完毕。

仅仅注册了,还不够,如果要使用,我们要先唤醒这个被注册了的softirq,那么就需要raise_softirq(名字就叫唤醒软中断,很好记),可以看出这个函数就是唤醒了那个你刚才注册在数组里的第nr项的处理函数,等处理中断时,这个函数就会被执行。

void raise_softirq(unsigned int nr)
{
	unsigned long flags;
	local_irq_save(flags);
	raise_softirq_irqoff(nr);
	local_irq_restore(flags);
}

这里有解释一下里面调用的几个函数

1.local_irq_save 这个函数做了什么呢,看看源码里是这样写的。

#define local_irq_save(flags) \
do { \
raw_local_irq_save(flags);\
} while (0)    里面调用了raw_local_irq_save,go on -> 

里面还调用了不少,关键函数是arch_local_irq_save,顾名思义,该函数与架构有关,那么就看看x86下是个什么节奏。

static inline notrace unsigned long arch_local_irq_save(void)
{
	unsigned long flags = arch_local_save_flags();
	arch_local_irq_disable();
	return flags;
}

可看出,关掉了本地中断。

2. raise_softirq_irqoff函数

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


看看里面的注释,哦,是这样啊,如果我们在处理完一个中断或者软中断时,我们会运行软中断或者处于中断中我们要唤醒ksoftirqd线程来在之后处理这个软中断。

以上都是为软中断触发前做的准备,下面就要触发软中断了也就是__do_softirq,这里不急,我们先说一下tasklet。

tasklet是通过软中断实现的,由tasklet_struct结构表示。 由tasklet_schedule和 tasklet_hi_schedule来调度。

void __tasklet_schedule(struct tasklet_struct *t)
{
	unsigned long flags;

	local_irq_save(flags);
	t->next = NULL;
	*__this_cpu_read(tasklet_vec.tail) = t;
	__this_cpu_write(tasklet_vec.tail, &(t->next));
	raise_softirq_irqoff(TASKLET_SOFTIRQ);
	local_irq_restore(flags);
}

这里拿tasklet_schedule来举例,里面调用了__tasklet_schedule,其实调度,说的简单一点,就是把这个tasklet_struct的实例插入到tasklet_vec的链表的尾部,然后在之后的操作中就会执行这个tasklet了。那么,为什么插入到链表中就会执行了呢?请看下面

static void tasklet_action(struct softirq_action *a)
{
	struct tasklet_struct *list;

	local_irq_disable();
	list = __this_cpu_read(tasklet_vec.head);
	__this_cpu_write(tasklet_vec.head, NULL);
	__this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);
	local_irq_enable();

	while (list) {
		struct tasklet_struct *t = list;

		list = list->next;

		if (tasklet_trylock(t)) {
			if (!atomic_read(&t->count)) {
				if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
					BUG();
				t->func(t->data);
				tasklet_unlock(t);
				continue;
			}
			tasklet_unlock(t);
		}

		local_irq_disable();
		t->next = NULL;
		*__this_cpu_read(tasklet_vec.tail) = t;
		__this_cpu_write(tasklet_vec.tail, &(t->next));
		__raise_softirq_irqoff(TASKLET_SOFTIRQ);
		local_irq_enable();
	}
}
还记得之前的softirq_action么,也就是注册软中断时的行为的一个结构体。这里的tasklet_action就符合其要求。

的确,内核也通过open_softirq(TASKLET_SOFTIRQ, tasklet_action); 注册了它,那么在触发tasklet时就会执行该代码,里面就是把list里的tasklet_struct执行一遍,直到list为空,那么之前使用tasklet_schedule挂进list的tasklet_struct也会被执行,就相当于被调度了。


最后,让我们来看看关键代码,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);
}


h = softirq_vec;

h->action(h);	执行链表里的action。
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值