Linux内核中断处理机制--初始化

1、Linux内核从start_kernel函数开始进行初始化

(1)trap_init主要就是异常的初始化。

(2)init_IRQ才是中断的初始化。 

注意:ffff0000并不对应实际的物理内存,是虚拟地址。当建立地址映射之后,就需要把物理地址对应的向量表拷贝到虚拟地址ffff0000

 2、trap_init都干了啥呢?

void __init trap_init(void)
{
.... 把向量表拷贝到ffff0000地址
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
.... 把复杂向量表拷贝到ffff0200
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
}

3、__vectors_start和__stubs_start分别对应什么东西
①发生异常先到vectors地址找跳转代码
__vectors_start: 第一阶段的跳转
     swi SYS_ERROR0 /* 复位时,CPU将执行这条指令 */
     b vector_und + stubs_offset /* 未定义指令 */
     ldr pc, .LCvswi + stubs_offset /* swi异常 */
     b vector_pabt + stubs_offset /* 指令预取中止 */
     b vector_dabt + stubs_offset /* 数据访问中止 */
     b vector_addrexcptn + stubs_offset /* 没有用到 */ 
     b vector_irq + stubs_offset /* irq异常 */
     b vector_fiq + stubs_offset /* fiq异常 */

②上面这些表示发生异常时,要跳转去执行的代码。(举例 vector_und说明)stubs实际上一个宏,宏值里面处理了很多事情。
vector_und, UND_MODE /* "vector_stub" 是一个宏,根据 "und, UND_MODE" 定义一段代码*/
上面这段代码会处理很多东西,最后决定跳转到底下的哪个分支。
 /* 下面这些代码是跳转去执行更复杂的代码 */
     .long __und_usr @ 0 (USR_26 / USR_32) /* 用户模式下执行了未定义指令 */
     .long __und_invalid @ 1 (FIQ_26 / FIQ_32)
     .long __und_invalid @ 2 (IRQ_26 / IRQ_32)
     .long __und_svc @ 3 (SVC_26 / SVC_32) /* 在管理模式下执行了未定义指令 */

     ...

③__und_usr以后又会跳转到可执行的C处理函数(其他异常处理同理)
__und_usr: /* __und_usr 用户模式下发生未定义指令异常的处理代码 */ 
        usr_entry 
        tst r3, #PSR_T_BIT @ Thumb mode?
        bne __und_usr_unknown @ ignore FP //跳转到__und_usr_unknown 
        sub r4, r2, #4 
        ........ //下面还有一些汇编指令

__und_usr_unknown: 
        mov r0, sp /* 将栈顶地址,作为参数传入 */ 
        adr lr, ret_from_exception /* 将返回地址写入到 r0, 处理完C函数后将返回到这里 */
        b do_undefinstr /*C函数入口,处理未定义指令异常*/

同理:vector_irq最终跳转到asm_do_IRQ函数

以上2、3说明了两件事情:(1)trap_init的异常初始化只是把向量表(就是好几个跳转指令)复制到高地址ffff0000; (2)异常发生后,会直接跳到异常向量表里去寻找对应的分支,例如中断的话最终就跳到了asm_do_IRQ函数。也就是说上述两件事情完全是两码事情,一个是初始化,一个动作发生后的处理。

3、中断最终跳转到asm_do_IRQ函数
使用中断结构体数组irq_desc[]来描述一个中断,这样在初始化start_kernel的时候就可以

 中断处理的流程:
①发生中断时,cpu响应执行异常向量表__vectors_start  
②接着到--》vector_irq中断代码(在这里会计算返回值,保存一些寄存器,进入管理模式)
③vector_irq最后会调用中断处理总入口asm_do_IRQ
④asm_do_IRQ根据中断号调用irq_desc[??]数组项的处理函数handle_irq
⑤handle_irq接着会调用irq_desc[??]中chip成员设置硬件,比如清除中断,禁止中断,重新使能中断
⑥handle_irq会逐个调用action成员链表中注册的处理函数

4、再回到初始化init_IRQ
void __init init_IRQ(void)
{
    for (irq = 0; irq < NR_IRQS(宏值16); irq++) 将每个中断结构体数组元素都初始化状态
     {

        irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;

     }
    init_arch_irq();这个地方一个全局函数的地址。初始化为空,因为这里应该是初始化为架构相关的初始化函数,这里是中断处理的初始化函数s3c24xx_init_irq, 这里确实没看懂??
}
void (*init_arch_irq)(void) __initdata = NULL;  谁把这函数地址赋值s3c24xx_init_irq函数?????
5、先跳过去,看看s3c24xx_init_irq都干了啥?这里也就是芯片侧所需要的处理
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);初始化设置chip,以后就可以设置irq_desc[??].chip成员了
        set_irq_handler(irqno, handle_edge_irq);初始化设置处理函数,**发生中后handle_edge_irq会调用用户注册的函数了
        set_irq_flags(irqno, IRQF_VALID);设置它生效
    }
}

static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
    __set_irq_handler(irq, handle, 0, NULL);
}

void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name)
{
    desc->handle_irq = handle;也就说最终irq_desc[???].hander_irq 指向了-->handle_edge_irq才是最终的处理函数
    desc->name = name;
}

6、内核侧已经将handle_edge_irq的处理过程定义到了action中的处理函数了
static struct irq_chip s3c_irqext_chip = {  全局结构体
    .name        = "s3c-ext",
    .mask        = s3c_irqext_mask,
    .unmask        = s3c_irqext_unmask,
    .ack        = s3c_irqext_ack,
    .set_type    = s3c_irqext_type,
    .set_wake    = s3c_irqext_wake
};

void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
        /* Start handling the irq */
        desc->chip->ack(irq);
        /* Mark the IRQ currently in progress.*/
        desc->status |= IRQ_INPROGRESS;
        do {
                    struct irqaction *action = desc->action;循环处理每个action的处理函数
                    action_ret = handle_IRQ_event(irq, action);

        } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
}


irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
        do{
            ret = action->handler(irq, action->dev_id);
            action = action->next;
        } while (action);

    return retval;
}

7、用户注册(驱动程序)中断处理函数的过程
用户通过request_irq函数向内核注册中断函数,request_irq函数根据中断号找到irq_desc数组项,然后在它的action链表中添加一个选项。linux-2.6.22\kernel\irq\manage.c
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);
}
int setup_irq(unsigned int irq, struct irqaction *new)
{
①将新建的链表结构action成员加入链表,如果链表空就直接加入,否则先判断新建的action和链表中的action结构体中段类型是否一致;
是否都生命了共享中断,触发方式(flags),如果一致就加入链表
②设置irq_desc[irq]结构中的chip成员还没设置的指针,让他们指向默认函数。
③设置中断触发方式desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);
④启用中断desc->status & IRQ_NOAUTOEN:即注册就已经是能了
        irq_chip_set_defaults(desc->chip);
        new->irq = irq;
        register_irq_proc(irq);
        new->dir = NULL;
        register_handler_proc(irq, new);
}

8、再回头看(3)中断最终跳转到asm_do_IRQ函数
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc = irq_desc + irq;
    irq_enter();

    desc_handle_irq(irq, desc);直接调用了irq_desc[???]对应的handle_irq成员
}
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

9、中断的初始化和中断的发生对比

中断初始化:确定中断发生的时候应该跳哪个地址

中断发生:cpu放弃当前任务,跳到中断向量表;

        b verctor_irq + stubs_offset;

        然后跳到verctor_irq(执行一系列操作)

        最后跳到do_asm_IRQ;

void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    desc_handle_irq(irq, desc);
}

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
	desc->handle_irq(irq, desc);
}
也就说中断发生后,直接指向了desc[??]数组的第一个元素:即处理函数desc->handle_irq

那么handle_irq又是谁来设置的呢?????这就决定了最终处理函数是处理谁了!!!!!

void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
		  const char *name)
{
    。。。。
	desc->handle_irq = handle;
}

static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
	__set_irq_handler(irq, handle, 0, NULL);
}


void set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip,
			 irq_flow_handler_t handle)
{
	set_irq_chip(irq, chip);
	__set_irq_handler(irq, handle, 0, NULL);
}

void set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
			      irq_flow_handler_t handle, const char *name)
{
	set_irq_chip(irq, chip);
	__set_irq_handler(irq, handle, 0, name);
}

set_irq_chip_and_handler_name和set_irq_chip_and_handler函数最终就是被芯片相关的初始化调用的。我们可以发现很多地方都有进行设置handler函数

./arch/arm/plat-s3c24xx/irq.c:725:              set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:736:              set_irq_handler(irqno, handle_edge_irq);
./arch/arm/plat-s3c24xx/irq.c:756:              set_irq_handler(irqno, handle_edge_irq);
./arch/arm/plat-s3c24xx/irq.c:763:              set_irq_handler(irqno, handle_edge_irq);
./arch/arm/plat-s3c24xx/irq.c:774:              set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:781:              set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:788:              set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:795:              set_irq_handler(irqno, handle_edge_irq);

void __init s3c24xx_init_irq(void)
{
。。。
	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);
	}
。。。
}

handle_edge_irq才是最终的处理函数。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LuckyDog0623

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值