一、中断作用
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) { ... /* 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链表头,用户注册的处理函数都链接到这个链表里面,发生中断的时候,就会从之里面调用用户注册进来的中断服务函数