您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > Linux中断处理过程浅析
linux中断响应和处理过程:首先中断属于异常的一种。异常,就是可以打断CPU正常运行流程的一些事情,比如说外部中断,未定义的指定,试图修改只读数据,执行SWI指定(softwareinterruptinstructin,软件中断指令,比如说上层调用sys_read,sys_write就会产生swi)等。内核启动时在start_kernel函数(init/main.c)中调用trap_init,init_IRQ两个函数来设置异常的处理函数。trap_init函数(arch/arm/kernel/traps.c)void_inittrap_init(void){......memcpy((void*)vectors,__vectors_start,__vectors_end-__vectors_start);memcpy((void*)vectors+0x200,__stubs_start,__stubs_end-__stubs_start);.......}上面两条定义的是异常向量的存放地方,即:__stubs_start~~~~~__stubs_end之间就是异常向量.接下来我们看异常向量之间的定义:(arch/arm/kernel/entry-armv.s).equstubs_offset,__vectors_start+0x200-__stubs_start.globl__vectors_start__vectors_start:ARM(swiSYS_ERROR0)//复位时.CPU交执行这条指令THUMB(svc#0)THUMB(nop)W(b)vector_und+stubs_offset//未定义异常时,CPU将执行这条跳转指令W(ldr)pc,.LCvswi+stubs_offset//swi异常W(b)vector_pabt+stubs_offset//指令预取止W(b)vector_dabt+stubs_offset//数据访问中止W(b)vector_addrexcptn+stubs_offset//没有用到W(b)vector_irq+stubs_offset//irq中断W(b)vector_fiq+stubs_offset//fig中断(快速中断).globl__vectors_end__vectors_end:各种异常的处理函数可以分为五类,分别分布在下面不同的文件中:1、arch/arm/kernel/traps.c中处理未定义指令异常,总入口函数为do_undefinstr2、arch/arm/mm/fault.c与内存访问相关的异常,总入口函数为do_DataAbort,do_PretftchAbort3.arch/arm/mm/irq.c中断处理函数在这个文件中定义,总入口函数为asm_do_IRQ4.arch/arm/kernel/call.sswi异常处理比如说:sys_read,sys_open等.5.没有使用的异常除了IRQ中断外(FIG中断linux一般不使用),所有的异常内核都定义了细致而完备的处理函数.所以我们这里关心的也只是上面红色部分,即:IRQ中断.Init_IRQ函数(arch/arm/kernel/irq.c),被用来初使化中断的处理框架,设置各种中断的默认处理函数.Linux内核将所有中断统一编号,使用irq_desc结构来描述中断:每个数组项对应一个中断(也可能是一组中断,它们使用共同的中断号),里面记录了中断的名称,中断状态,中断标记,并提供硬件访问函数(清除,屏蔽,使能中断),提供了这个中断的处理函数的入口,通过它可以调用用户注册的中断处理函数include/linux/irq.h{.........irq_flow_handler_thandle_irq;//当前的中断处理函数入口structirq_chip*chip;//底层的硬件访问..........structirqaction*action;//用户提供的中断处理函数链表unsignedintstatus;//IRQ状态...........constchar*name;//中断名称}____cacheline_internodealigned_in_smp;Handle_irq是这个或者这组中断的处理函数入口.当中断发生时总中断入口函数asm_do_IRQ将根据中断号调用相应irq_desc数组中的handle_irq函数,handle_irq使用chip结构中的函数来清除,屏蔽,使用中断,还会一一调用用户在action链表中注册的中断处理函数.Structirq_chip{constchar*name;//启动中断,如果不设置,缺省为enableunsignedint(*startup)(unsignedintirq);//启动中断,如果不设置,缺省为enablevoid(*shutdown)(unsignedintirq);//关闭中断,如果不设置,缺省为disablevoid(*enable)(unsignedintirq);//使能中断,如果不设置,缺省为unmaskvoid(*disable)(unsignedintirq);//禁止中断如果不设置,缺省为maskvoid(*ack)(unsignedintirq);//响应中断,通常是清除当前中断使得可以接收下一个中断void(*mask)(unsignedintirq);//屏蔽中断源void(*mask_ack)(unsignedintirq);//屏蔽和响应中断void(*unmask)(unsignedintirq);//开启中断源..........}structirqaction*action;结构类型在include/linux/interrupt..h中定义.用户注册的每一个中断处理函数都用一个irqaction结构表示,一个中断(比如共享中断)可以有多个处理函数,它们的irqacion结构链接成一个链表,以action为表头.structirqaction{irq_handler_thandler;//用户注册的中断处理函数unsignedlongflags;//中断标志,比如是否为共享中断,电平触发还是边沿触发constchar*name;//用户注册的中断名字void*dev_id;//用户供给的handle参数,还可以区分共享中断structirqaction*next;intirq;//中断号structproc_dir_entry*dir;irq_handler_tthread_fn;structtask_struct*thread;unsignedlongthread_flags;};Irq_desc结构数组中:irq_flow_handler_thandle_irq,structirq_chip*chip,struct,irqaction*action这三种数据结构构成了中断处理体系结构.很明显,中断需要用户处理的只有最后一步,就是用户的action中断处理函数.所以我们需要告诉内核我们相应的中断处理函数在哪里,中断注册:reguest_irq,相对应的中断卸载:free_irq.intrequest_irq(unsignedintirq,//中申请中断的中断号,可以根据不用的引脚到irqs.h里面查找irqreturn_t(*handler)(int,void*,structpt_regs*),//用户写的中断处理函数unsignedlongirqflags,//中断触发方式(高电平触发,低电平触发,上升沿,下降沿等)constchar*devname,//中断名字(自己取名)void*dev_id);//dev_id(共享中断时,用于识别倒里是哪个硬件产生的中断)//同一中断的不同处理函数必须用dev_id来区分,//共享中断之间既可使用同一中断处理函数,也可使用不同中断处理函数,都需要dev_id区分.中断注册做了三件事:1.提供用户action中断处理函数链接2.中断触发方式是什么3.中断使能Voidfree_irq(unsignedintirq,//中断号与注册时对应void*dev_id)//共享中断时用于区分不同硬件与注册时对应中断卸载和注册相反:1.根据中断号irq,dev_id从action链表中找到表项,将它删除2.如果它是唯一表项,还要调用IRQ_DESC[IRQ].CHIP-SHUTDOWN或DESC[IRQ].CHIP-DISABLE来关闭中断.所以很显然,用户要自己写一个中断程序,只需要实现三步,1.向内核申请注册中断2.实现用户中断处理函数3.在不需要该中断的时候卸载中断附上一个例子:按键的中断程序驱动程序:#includelinux/module.h#includelinux/kernel.h#includelinux/fs.h#includelinux/init.h#includelinux/delay.h#includeasm/irq.h#includelinux/interrupt.h#includeasm/uaccess.h#includeasm/arch/regs-gpio.h#includeasm/hardware.h#defineDEVICE_NAMEbuttons/*加载模式后,执行”cat/proc/devices”命令看到的设备名称*/#defineBUTTON_MAJOR232/*主设备号*/structbutton_irq_desc{intirq;unsignedlongflags;char*name;};/*用来指定按键所用的外部中断引脚及中断触发方式,名字*/staticstructbutton_irq_descbutton_irqs[]={{IRQ_EINT19,IRQF_TRIGGER_FALLING,KEY1},/*K1*/{IRQ_EINT11,IRQF_TRIGGER_FALLING,KEY2},/*K2*/{IRQ_EINT2,IRQF_TRIGGER_FALLING,KEY3},/*K3*/{IRQ_EINT0,IRQF_TRIGGER_FALLING,KEY4},/*K4*/};/*按键被按下的次数(准确地说,是发生中断的次数)*/staticvolatileintpress_cnt[]={0,0,0,0};/*等待队列:*当没有按键被按下时,如果有进程调用s3c24xx_buttons_read函数,*它将休眠*/staticDECLARE_WAIT_QUEUE_HEAD(button_waitq);/*中断事件标志,中断服务程序将它置1,s3c24xx_buttons_read将它清0*/staticvolatileintev_press=0;staticirqreturn_tbuttons_interrupt(intirq,void*dev_id){volatileint*press_cnt=(volatileint*)dev_id;*press_cnt=*press_cnt+1;/*按键计数加1*/ev_press=1;/*表示中断发生了*/wake_up_interruptible(&button_waitq);/*唤醒休眠的进程*/returnIRQ_RETVAL(IRQ_HANDLED);}/*应用程序对设备文件/dev/buttons执行open(...)时,*就会调用s3c24xx_buttons_open函数*/staticints3c24xx_buttons_open(structinode*inode,structfile*file){inti;interr;for(i=0;isizeof(button_irqs)/sizeof(button_irqs[0]);i++){//注册中断处理函数err=request_irq(button_irq
本文标题:Linux中断处理过程浅析
链接地址:https://www.777doc.com/doc-2880873 .html