linux 中断管理(一)
一、中断作用
Linux 内核需要对连接到计算机上的所有硬件设备进行管理。如果要管理这些设备,首先得和它们互相通信才行。
一般有两种方案可实现这种功能:
- 轮询(polling) 让内核定期对设备的状态进行查询,然后做出相应的处理;
- 中断(interrupt) 让硬件在需要的时候向内核发出信号(变内核主动为硬件主动)。
使用轮询的方式会占用CPU比较多的时间,效率极低。例如:要读取一个按键有没有被按下时,一个进程需要不断地查询按键有没有被按下。这样这个任务就占用CPU大量得时间,使得CPU做了大量的无用功。使用中断提供这样的一个机制。当按键没有被按下的时候,挂起当前进程,将控制权转交给其他进程。当按键按下的时候,操作系统把当前进程设为活动的,从而允许该进程继续执行。
二、linux中断管理
linux 内核将所有的中断统一编号,使用一个
irq_desc
结构体数组描述中断。一个数组项对用一个中断(或者是一组中断,它们共用中断号)。
struct irq_desc
结构体记录了,中断的名称、中断状态,底层硬件访问接口(使能中断,屏蔽中断,清除中断),中断处理函数的入口,
通过它可以调用用户注册的中断处理函数。
1、struct irq_desc
struct irq_desc
在include\linux\irq.h
文件里面定义
struct irq_desc {
irq_flow_handler_t handle_irq; /* 当前中断的处理函数入口 */
struct irq_chip *chip; /* 底层硬件访问 */
...
struct irqaction *action; /* 用户注册的中断处理函数链表 */
unsigned int status; /* IRQ状态 */
...
const char *name; /* 中断函数名 */
} ____cacheline_internodealigned_in_smp;
a. handle_irq
handle_irq
是这个或者是这组的中断的处理函数入口。发生中断时,会调用asm_do_IRQ
函数。在这个函数里面根据中断号调用相应irq_desc
数组项的handle_irq
。
在handle_irq
里面会使用chip
成员的接口来使能、屏蔽、清除中断。还会一一调用用户注册在action
链表里面的处理函数。
b. struct irq_chip
struct irq_chip 在
include\linux\irq.h
文件里面定义
struct irq_chip {
const char *name;
/* 启动中断,如果不设置则缺省为 "enable" */
unsigned int (*startup)(unsigned int irq);
/* 关闭中断,如果不设置则缺省为 "disable" */
void (*shutdown)(unsigned int irq);
/* 使能中断,如果不设置则缺省为"unmask" */
void (*enable)(unsigned int irq);
/* 禁止中断,如果不设置则缺省为"mask" */
void (*disable)(unsigned int irq);
/* 响应中断,一般是清除当前的中断,使得可以接收下一个中断 */
void (*ack)(unsigned int irq);
/* 屏蔽中断源 */
void (*mask)(unsigned int irq);
/* 屏蔽和响应中断 */
void (*mask_ack)(unsigned int irq);
/* 开启中断 */
void (*unmask)(unsigned int irq);
....
};
c. struct irqaction
struct irqaction
结构体在include\linux\interrupt.h
文件里面定义。
用户注册的每个中断处理函数都用一个irqaction
结构体来描述一个中断(例如共享中断)可以有多个处理函数。
它们的irqaction
结构以action
为表头链成一个链表
struct irqaction {
/* 用户注册的中断处理函数 */
irq_handler_t handler;
/* 中断的标志,是否是共享中断,中断的触发方式是电平触发,还是边沿触发 */
unsigned long flags;
cpumask_t mask;
/* 用户注册时,给的中断的名字 */
const char *name;
/* handler 中断函数的参数,也可以用来区分共享中断 */
void *dev_id;
/* 链表的指针 */
struct irqaction *next;
/* 中断号 */
int irq;
struct proc_dir_entry *dir;
};
2. 小结
对于
struct irq_desc
数组 、struct irq_chip
结构体 和struct irqaction
三者之间的关系,如下图:
三、中断处理初始化
1、中断处理初始化
在
init\Main.c
文件的start_kernel
函数里面调用的init_IRQ()
函数就是中断体系结构的初始化
2、init_IRQ 函数
init_IRQ
用来初始化中断处理体系结构, 在arch\arm\kernel\irq.c
文件里面
void __init init_IRQ(void)
{
int irq;
/* 初始化irq_desc[] 每一项的中断状态 */
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
...
/* 架构相关的中断初始化函数 */
init_arch_irq();
}
3、 init_arch_irq
init_arch_irq
是一个函数指针,arch\arm\kernel\setup.c
文件setup_arch()
函数被初始化
void __init setup_arch(char **cmdline_p)
{
...
init_arch_irq = mdesc->init_irq;
...
}
mdesc->init_irq
指向的是arch\arm\plat-s3c24xx\irq.c
文件的s3c24xx_init_irq()
函数。
MACHINE_START(S3C2440, "SMDK2440")
是一个宏,用来定义struct machine_desc
结构体
结构体在arch\arm\mach-s3c2440\Mach-smdk2440.c
文件里面定义并且初始化init_irq
指向s3c24xx_init_irq()
函数
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
/* init_irq成员在这里初始化 */
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
4、s3c24xx_init_irq函数
s3c24xx_init_irq()
函数在arch\arm\plat-s3c24xx\irq.c
定义(部分代码如下面的代码块),他为所有的所有与芯片操作相关的数据结构(irq_desc[irq].chip),,并且初始化了处理函数入口(irq_desc[irq].handle_irq)。
以IRQ_EINT0
和IRQ_EINT3
为例,set_irq_chip
函数就是将irq_desc[irqno].chip = &s3c_irq_eint0t4
,以后就可以通过irq_desc[irqno].chip
结构的函数指针来设置触方式,使能中断、屏蔽中断了
set_irq_handler
函数就是设置中断处理函数入口 将irq_desc[irqno].handle_irq = handle_edge_irq
发生中断时,handle_edge_irq
会调用用户具体注册的处理函数
irq_desc[irqno].falgs
设置为IRQF_VALID
void __init s3c24xx_init_irq(void)
{
/* 中断号是组的初始化 */
set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
...
/* external interrupts */
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)\n", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d (extended s3c irq)\n", irqno);
set_irq_chip(irqno, &s3c_irqext_chip);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
...
}
5、set_irq_chip函数
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
struct irq_desc *desc;
unsigned long flags;
/* 判断是否超过最大的中断号 */
if (irq >= NR_IRQS) {
printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq);
WARN_ON(1);
return -EINVAL;
}
if (!chip)
chip = &no_irq_chip;
/* 通过中断号找到irq_desc数组对应的数组项 */
desc = irq_desc + irq;
spin_lock_irqsave(&desc->lock, flags);
/* 判断 chip 的成员即&s3c_irq_eint0t4的成员是否为空,如果为空就设置为默认的操作函数 */
irq_chip_set_defaults(chip);
/* 设置irq_desc[].chip成员 */
desc->chip = chip;
spin_unlock_irqrestore(&desc->lock, flags);
return 0;
5、set_irq_handler函数
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 0, NULL);
}
/***************************************************************************************/
__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
struct irq_desc *desc;
unsigned long flags;
/* 通过中断号找到irq_desc数组对应的数组项 */
desc = irq_desc + irq;
...
/* 中间还会做一些判断 */
...
/* 设置中断处理函数,名字*/
desc->handle_irq = handle;
desc->name = name;
/* 设置中断的状态,开启中断 */
if (handle != handle_bad_irq && is_chained) {
desc->status &= ~IRQ_DISABLED;
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
desc->depth = 0;
desc->chip->unmask(irq);
}
}
6、set_irq_flags函数
void set_irq_flags(unsigned int irq, unsigned int iflags)
{
struct irq_desc *desc;
unsigned long flags;
if (irq >= NR_IRQS) {
printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
return;
}
/* 找到数组项 */
desc = irq_desc + irq;
spin_lock_irqsave(&desc->lock, flags);
/* 设置中断状态 */
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
if (iflags & IRQF_VALID)
desc->status &= ~IRQ_NOREQUEST;
if (iflags & IRQF_PROBE)
desc->status &= ~IRQ_NOPROBE;
if (!(iflags & IRQF_NOAUTOEN))
desc->status &= ~IRQ_NOAUTOEN;
spin_unlock_irqrestore(&desc->lock, flags);
}
四、总结
中断处理体系结构的初始化的过程其实就是对
irq_desc[]
数组的每一项初始化进行初始化.
一个中断或者一组中断通过irq_desc[]
的一个数组项来管理。
数组项里面的handle_irq
chip
action
三个重要的结构体成员。
- handle_irq是当前的中断处理函数
- chip底层硬件相关的处理函数(设置中断的触发方式,屏蔽中断,使能中断)
- action链表头,用户注册的处理函数都链接到这个链表里面,发生中断的时候,就会从之里面调用用户注册进来的中断服务函数