Linux内核源码阅读之中断(简)

摘要

本文对Linux2.4.0中中断机制从源码层面进行简要介绍,阅读需要有一定基础,详细版本请参考这里

这里主要介绍以下几个部分:

1.中断向量表IDT的初始化

主要是设置中断向量表中中断服务的类型,服务程序的入口地址,DPL等。

1.1 trap_init()初始化系统保留的中断向量,从0x00到0x1f共36个;init_IRQ()初始化用于外设的通用中断向量,0x20~0xff共224个,第0x80向量用于系统调用中断,不包含在内。

1.2设置通用中断向量的服务程序的入口地址interrupt[224]。

1.3对于用于外设的通用中断向量,需要结构irq_desc[224],每个元素代表一个中断请求队列,具有相同“中断请求号(0x20~0xff)”的中断请求需要挂到相应的irq_desc中。

2.中断请求队列的初始化(只说明通用中断)

某个设备的初始化程序将其中断服务程序通过request_irq()挂入到某个中断请求队列irq_desc中。

3.中断的响应和服务

3.1以IRQ0x03_interrup为例,首先将其中断请求号0x03-256压栈,调用common_interrupt,主要是保护现场,将所有寄存器压栈,将中断返回地址ret_from_intr压栈,调用主服务do_IRQ(struct pt_regs regs)

3.2从参数regs中获取关键的中断请求号irq,根据irq找到相应的中断请求队列irq_desc[irq],执行队列中的服务程序。

4.软中断

软中断的目的是为了一个中断服务占用过长cpu时间,将中断服务分成两部分,第一部分仅仅完成一些关键的操作,然后就开中断,中断服务第二部分可能耗时比较长,就将其放在开中断下进行。

我们知道正常的中断处理程序是在do_IRQ中完成的,而软中断的第二部分则是在每次中断处理完do_IRQ后判断是否有软中断请求,进行进入软中断第二部分的服务程序。

这里我们需要struct softirq_action softirq_vec[32]数据结构

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

它和irq_desc[224]类似,但是刻印看出它并不是一个队列,里面仅仅保存的是不同软中断的入口函数指针,这些函数指针在softirq_init()中设置。

在软中断初始化时,系统执行 softirq_init()

open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);

open_softirq()

softirq_vec[nr].data = data;
softirq_vec[nr].action = action;

for (i=0; i<NR_CPUS; i++)
	softirq_mask(i) |= (1<<nr);
spin_unlock_irqrestore(&softirq_mask_lock, flags);

将tasklet_action和tasklet_hi_action两种软中断初始化到softirq_vec[32]数组中。而且由于多处理器的原因,需要数据结构irq_cpustat_t irq_stat[NR_CPUS]

typedef struct {
	unsigned int __softirq_active;//软中断请求寄存器
	unsigned int __softirq_mask;//软中断屏蔽寄存器
	unsigned int __local_irq_count;
	unsigned int __local_bh_count;
	unsigned int __syscall_count;
	unsigned int __nmi_count;	/* arch dependent */
} ____cacheline_aligned irq_cpustat_t;

来表示每个CPU的软中断请求寄存器,软中断屏蔽寄存器,可以看出软中断的中断是以软件的方式实现的。这样哪个CPU发生的软中断由自己进行处理。open_soft_irq()中最后for循环就是将第TASKLET_SOFTIRQ和HI_SOFTIRQ中断屏蔽位置1,表明允许中断。

对于tasklet_action和tasklet_hi_action两种软中断,每个cpu需要软中断服务程序队列头,使得可以将第二部分的中断服务程序挂到相应cpu队列头上。这个数据结构为tasklet_vec[NR_CPUS]和tasklet_hi_vec[NR_CPUS]

struct tasklet_head
{
	struct tasklet_struct *list;
} __attribute__ ((__aligned__(SMP_CACHE_BYTES)));

我们说要将软中断服务程序挂到相应的cpu队列头中,这个数据结构就是tasklet_struct

struct tasklet_struct
{
	struct tasklet_struct *next;//挂到某个cpu队头
	unsigned long state;
	atomic_t count;
	void (*func)(unsigned long);//具体中断服务程序
	unsigned long data;
};

当内核执行完do_IRQ()后,检查是否有软中断请求,并通过do_softirq()执行。

具体就是先获取当前cpu编号,通过

active = softirq_active(cpu) & mask;

查看当前cpu是否有可执行未屏蔽的软中断。active的每一位对应一个软中断号,与softirq_vec数组中的每一项对应,因此通过

h = softirq_vec;
mask &= ~active;


do {
	if (active & 1)
		h->action(h);
	h++;
	active >>= 1;
} while (active);

 

依次执行未屏蔽已发出请求的软中断,我们知道,之前softirq_vec数组中设置了两个软中断服务类型:tasklet_action和tasklet_hi_action,h->action(h)可能就执行了其中一个。我们以tasklet_action为例。

static void tasklet_action(struct softirq_action *a)
{
	int cpu = smp_processor_id();
	struct tasklet_struct *list;

	local_irq_disable();
	list = tasklet_vec[cpu].list;
	tasklet_vec[cpu].list = NULL;
	local_irq_enable();

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

		list = list->next;

		if (tasklet_trylock(t)) {
			if (atomic_read(&t->count) == 0) {
				clear_bit(TASKLET_STATE_SCHED, &t->state);

				t->func(t->data);

#ifdef CONFIG_SMP
				smp_mb__before_clear_bit();
#endif
				tasklet_unlock(t);
				continue;
			}
			tasklet_unlock(t);
		}
		local_irq_disable();
		t->next = tasklet_vec[cpu].list;
		tasklet_vec[cpu].list = t;
		__cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
		local_irq_enable();
	}
}

主要就是先获取是哪个cpu要执行软中断服务,获取相应cpu软中断服务队列头tasklet_vec[cpu].list,依次执行队列中tasklet_struct结构中func函数指针指向的真正的中断服务程序。

linux中断机制结构图

 

参考资料:

《Linux内核情景分析》毛德操,胡希明

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值