1.中断处理体系结构

Linux内核将所有中断统一编号,使用一个irq_desc结构数组来描述这些中断。

数组声明在/linux/kernel/irq/handle.c中,其中#define NR_IRQS 128,定义在/linux/include/asm/irq.h中

 /*
* Linux has a controller-independent interrupt architecture.
* Every controller has a 'controller-template', that is used
* by the main code to do the right thing. Each driver-visible
* interrupt source is transparently wired to the appropriate
* controller. Thus drivers need not be aware of the
* interrupt-controller.
*
* The code is designed to be easily extended with new/different
* interrupt controllers, without having to do assembly magic or
* having to touch the generic code.
*
* Controller mappings for all interrupt sources:
*/
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[ ... NR_IRQS-] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = ,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
#ifdef CONFIG_SMP
.affinity = CPU_MASK_ALL
#endif
}
};
irq_desc结构的数据类型在/linuxinclude/linux/irq.h中定义,
 struct irq_desc {
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ status */ unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;

handle_irq是这个或这组中断的处理函数入口。发生中断时,总入口函数asm_do_IRQ将根据中断号调用相应irq_desc数组项中handle_irq.

typedef    void fastcall (*irq_flow_handler_t)(unsigned int irq,                struct irq_desc *desc);

handle_irq使用chip结构中的函数清除、屏蔽或者重新使能中断,还要调用用户在action链表中注册的中断处理函数。irq_chip结构类型也是在include/linux/irq.h中定义,其中的成员大多用于操作底层硬件,比如设置寄存器以屏蔽中断,使能中断,清除中断等。注意这里的成员name会出现在/proc/interrupts中。

struct irq_chip {
const char *name;
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
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);
void (*eoi)(unsigned int irq); void (*end)(unsigned int irq);
void (*set_affinity)(unsigned int irq, cpumask_t dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type);
int (*set_wake)(unsigned int irq, unsigned int on); /* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};

irq_desc结构中的irqaction结构类型在include/linux/iterrupt.h中定义。用户注册的每个中断处理函数用一个irqaction结构来表示,一个中断比如共享中断可以有多个处理函数,它们的irqaction结构链接成一个链表,以action为表头。irqation结构在linux/include/linux/interrupt.h中定义如下:

typedef irqreturn_t (*irq_handler_t)(int, void *);

struct irqaction {
irq_handler_t handler;
unsigned long flags;
cpumask_t mask;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
};

irq_desc结构数组、它的成员“struct irq_chip *chip” "struct irqaction *action",这3种数据结构构成了中断处理体系的框架。下图中描述了Linxu中断处理体系结构的关系图:

中断处理流程如下
(1)发生中断时,CPU执行异常向量vector_irq的代码
(2)在vector_irq里面,最终会调用中断处理的总入口函数asm_do_IRQ
(3)asm_do_IRQ根据中断号调用irq_desc数组项中的handle_irq。
(4)handle_irq会使用chip成员中的函数来设置硬件,比如清除中断、禁止中断、重新使能中断等
(5)handle_irq逐个调用用户在aciton链表中注册的处理函数
   中断体系结构的初始化就是构造这些数据结构,比如irq_desc数组项中的handle_irq、chip等成员;用户注册中断时就是构造action链表;用户卸载中断时就是从action链表中去除不需要的项。
 
2.中断处理体系结构的初始化
init_IRQ函数被用来初始化中断处理体系结构,代码在arch/arm/kernel/irq.c中
void __init init_IRQ(void)
{
int irq; for (irq = ; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE; #ifdef CONFIG_SMP
bad_irq_desc.affinity = CPU_MASK_ALL;
bad_irq_desc.cpu = smp_processor_id();
#endif
init_arch_irq();
}

下面简单分析下init_arch_irq();的获取过程及调用顺序

 /*
init_arch_irq()的由来
定义一个空函数指针void (*init_arch_irq)(void) __initdata = NULL;
*/
asmlinkage void __init start_kernel(void)
-->setup_arch(&command_line);
-->init_arch_irq = mdesc->init_irq;
-->init_IRQ();
-->init_arch_irq()//即mdesc->init_irq=s3c24xx_init_irq

上述machine_desc结构在/linux/arch/arm/mach-s3c2410/mach-bast.c如下宏中获得,后面会分析machine_desc结构。

MACHINE_START(BAST, "Simtec-BAST")
//Maintainer: Ben Dooks <ben@simtec.co.uk>
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> ) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = bast_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = bast_init,
.timer = &s3c24xx_timer,
MACHINE_END

对于S3C2440开发板,这个函数就是s3c24xx_init_irq,移植machine_desc结构中的init_irq成员就指向这个函数s3c24xx_init_irq函数在arch/arm/plat-s3c24xx/irq.c中定义,它为所有中断设置了芯片相关的数据结构(irq_desc[irq].chip),设置了处理函数入口(irq_desc[irq].handle_irq)。

 以外部中断EINT4-EINT23为例,用来设置它们的代码如下:
void __init s3c24xx_init_irq(void)
{
... 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);
}
... }

set_irq_chip函数的作用就是“irq_desc[irno].chip = &s3c_irqext_chip”,以后就可能通过irq_desc[irqno].chip结构中的函数指针设置这些外部中断的触发方式(电平触发,边沿触发),使能中断,禁止中断。

set_irq_chip函数的作用就是“irq_desc[irno].handler_irq = handle_edge_irq”,设置这些中断的处理函数入口为handle_edge_irq。发生中断时handle_edge_irq函数会调用用户注册的具体处理函数;

set_irq_flags设置中断标志为“IRQF_VALID”,表示可以使用它们。

init_IRQ函数执行完后,irq_desc数组项的chip,handl_irq成员都被设置。

3.用户注册中断过程分析

调用/linux/kernel/irq/manage.c中的request_irq函数。

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
-->struct irqaction *action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
-->action->handler = handler;
-->action->flags = irqflags;
-->cpus_clear(action->mask);
-->action->name = devname;
-->action->next = NULL;
-->action->dev_id = dev_id;
-->retval = setup_irq(irq, action);
-->request_irq函数根据中断号找到irq_desc数组项,然后在它的action链表添加一个表
-->irq_chip_set_defaults(desc->chip);//chip成员在init_IRQ()中已经设置,这里设置其中还没有设置的。
-->设置中断触发方式
-->启动中断

3.1将新建的irqaction结构链入irq_desc[irq]结构的action链表中

这有两种可能。如果action链表为空,则直接链入;否则先判断新建的irqaction结构和链表中的irqaction结构所表示的中断类型是否一致,即是否都声明为"可共享的"(IRQF_SHARED)、是否都使用相同的触发方式,如果一致,则将新建的irqation结构链入。
3.2设置irq_desc[irq]结构中chip成员的还没设置的指针

让它们指向一些默认函数。chip成员在init_IRQ函数初始化中断体系结构的时候已经设置了,这里只是设置其中还没设置的指针这通过irq_chip_set_defaults函数来完成,它在kernel/irq/chip.c中定义

3.3设置中断的触发方式

如果requestt_irq传入的irqflags参数表示中断的触发方式为高低电平触发/边沿触发,则调用irq_desc[irq]结构中的chip-.set_type成员函数来进行设置:设置引脚功能为外部中断,设置中断触发方式。

3.4启动中断

如果irq_desc[irq]结构中status成员没有被指明IRQ_NOAUTOEN(表示注册中断时不要使用中断),还要调用chip->startup或chip->enable来启动中断,所谓启动中断通常就是使用中断。一般情况下,只有那些“可以自动使能的”中断对应的irq_desc[irq].status才会被指明为IRQ_NOAUTOEN,所以,无论哪种情况,执行request_irq注册中断之后,这个中断就已经被使能了。

//-----------------------------------------------

/*
NR_IRQS定义in linux/arch/arm/plat-s3c64xx/include/mach/irqs.h
*/
#define NR_IRQS (IRQ_EINT_GROUP9_BASE + IRQ_EINT_GROUP9_NR + 1)

asmlinkage void __init start_kernel(void)
-->early_irq_init();
-->init_IRQ();
-->init_arch_irq();

/*
arch/arm/kernel/irq.c中声明init_arch_irq函数指针
*/
void (*init_arch_irq)(void) __initdata = NULL; /*全局函数指针*/
void __init setup_arch(char **cmdline_p)
-->init_arch_irq = mdesc->init_irq;//s3c6410_init_irq

/*
linux/arch/arm/mach-s3c6410/mach-smdk6410.c中定义machine_desc结构体
*/
MACHINE_START(SMDK6410, "SMDK6410")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C64XX_PA_SDRAM + 0x100,

.init_irq = s3c6410_init_irq,
.map_io = smdk6410_map_io,
.init_machine = smdk6410_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END

/*
s3c6410_init_irq in linux/arch/arm/mach-s3c6410/cpu.c
*/
void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
{
int uart, irq;

printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);

/* initialise the pair of VICs */
vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid);
vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid);

/* add the timer sub-irqs */

set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0);
set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1);
set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2);
set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3);
set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4);

for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) {
set_irq_chip(irq, &s3c_irq_timer);
set_irq_handler(irq, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}

for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++)
s3c64xx_uart_irq(&uart_irqs[uart]);
}

#define IRQ_EINT_GROUP(group, no) (IRQ_EINT_GROUP##group##_BASE + (no))
/*
IRQ_EINT_GROUP(1, 3)展开为
IRQ_EINT_GROUP1_BASE + 3
*/

Linux中断体系结构的更多相关文章

  1. ARM linux中断总结

    Linux异常处理体系结构 Linux异常体系之vector_stub宏解析 Linux异常体系之stubs_offset Linux中断体系结构 ARM系统调用

  2. Linux异常处理体系结构

    arm11处理器裸机的异常与中断处理参考: [OK6410裸机程序]异常处理 [OK6410裸机程序]按键中断 另外参考一篇:Linux中断体系结构 在ARM V4及V4T以后的大部分处理器中,中断向 ...

  3. Linux计时体系结构

    [Linux操作系统分析]定时测量——RTC,TSC,PIT,jiffies,计时体系结构,延迟函数   1 基本概念 定时机制连同一些更可见的内核活动(如检查超时)来驱使进程切换. 两种主要的定时测 ...

  4. Linux中断(interrupt)子系统之一:中断系统基本原理【转】

    转自:http://blog.csdn.net/droidphone/article/details/7445825 这个中断系列文章主要针对移动设备中的Linux进行讨论,文中的例子基本都是基于AR ...

  5. Linux中断概述

    中断和异常 1.1中断的由来及实质 Linux内核要管理计算机上的硬件设备,首先要和他们通信.而处理器的速度跟外围硬件设备的速度往往不在一个数量级上,因此,如果内核采取让处理器向硬件发出一个请求,然后 ...

  6. Linux中断(interrupt)子系统之二:arch相关的硬件封装层【转】

    转自:http://blog.csdn.net/droidphone/article/details/7467436 Linux的通用中断子系统的一个设计原则就是把底层的硬件实现尽可能地隐藏起来,使得 ...

  7. Linux中断(interrupt)子系统之一:中断系统基本原理

    这个中断系列文章主要针对移动设备中的Linux进行讨论,文中的例子基本都是基于ARM这一体系架构,其他架构的原理其实也差不多,区别只是其中的硬件抽象层.内核版本基于3.3.虽然内核的版本不断地提升,不 ...

  8. 字符设备驱动-----Linux中断处理体系结构

    一.中断处理体系结构的初始化 Linux内核将所有的中断统一编号,使用一个irq_desc结构数组来描述这些中断;每个数组项对应一个中断,也可能是一组中断,它们共用相同的中断号,里面记录了中断的名称. ...

  9. 1.字符设备驱动------Linux中断处理体系结构

    一.中断处理体系结构的初始化 Linux内核将所有的中断统一编号,使用一个irq_desc结构数组来描述这些中断;每个数组项对应一个中断,也可能是一组中断,它们共用相同的中断号,里面记录了中断的名称. ...

随机推荐

  1. Netty(1-2)Discard Client

    一.DiscardClientHandler import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; import ...

  2. hbase按照时间戳删除记录

    1.按照时间戳范围查询记录 echo "scan 'event_log', { COLUMN => 'cf:sid', TIMERANGE => [1466265600272, ...

  3. opencv 形态学膨胀和腐蚀以及开运算和闭运算

  4. vconsole移动端调试技巧(禁止webviuew,inspect等)

    如果由于某种原因(天朝FQ),不能支持google 的 inspect  调试 或者再想在某个APP里面调试你的页面,但是没有打开APP的webview ,也不能授权调试 在或者,Fider 可以拦截 ...

  5. WPF 模拟Button按钮事件触发

    this.Submit.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.Submit_Click)); //这种是无效的方法 thi ...

  6. Spring中统一相同版本的api请求路径的一些思考

    Spring中统一相同版本的api请求路径的一些思考 问题场景 当我们在实际开发中,可能会遇到开发相同同版本的api, 假设相同版本的api请求路径为/v1/functionA,/v1/functio ...

  7. AngularJS(八):http服务

    本文也同步发表在我的公众号“我的天空” http服务 之前我们的示例都是在本地获取模拟数据,在实际应用中,所有的项目都将不可避免的从后台获取数据,我们都是通过Ajax来实现与服务器的通信.在Angul ...

  8. 告别CMD.windows终端神器conemu设置

    前言 一种刘姥姥进大观园的感觉,现在是见啥啥新鲜.因为之前不怎么接触到命令操作,平时偶尔用用cmd也没觉得什么不妥.直到现在经常调试脚本,使用git越发感觉不方便.看见同事使用的terminal绚丽夺 ...

  9. 2017微软骇客马拉松精彩大回Fun:不一样的Hacker,一Young的Cool

    丹棱君有话说:一年一度激动人心的骇客马拉松大会结束了!这场内部创意大比拼硕果累累,丹棱君准备好了 6 组 Cool 骇客的别 Young 作品——沉浸式销售工具如何能守得“云”开见月明?“骇客马拉松超 ...

  10. UWP开发:应用文件存储

    应用设置由于数据量和数据类型的限制,有很大的局限性,所以还需要应用文件存储,以文件的方式存储数据.在每个应用的应用数据存储中,该应用拥有系统定义的根目录:一个用于本地文件,一个用于漫游文件,还有一个用 ...