Linux异常处理框架(基于Linux-3.4.2)

/*

*注意:本文仅用于学习记录和交流,若有错,望指正,谢谢

*/

--------------------------------------------------分割线------------------------------------------------------------

一、中断类型?中断源?中断控制器?中断大致处理流程?

        中断的分类(注意与快中断区分,Linux中也没有用到快中断异常):外部中断、内部中断。

        中断源决定了中断的类型,比如:按键等中断源是外部中断,定时器中断,系统调用(软中断)等为内部中断。

        中断控制器:PIC(可编程中断控制器)在SOC内部集成,并挂载在APB总线上,使用HCLK时钟。

                另:ARM多核处理器中常用的是GIC,支持三种类型的中断:

                        SGI:软件产生的中断,多用于核间通信。

                        PPI:某个CPU私有外设的中断,只能发给指定的CPU。

                        SPI:共享外设的中断,在后面源码中可以看到flag。

        中断大致处理流程:

                单片机怎么做的:中断发生(如按键中断)->CPU到中断向量地址去执行跳转->跳转到中断处理子程序->计算返回地址,保存现场,执行具体的处理,恢复现场->回到了被中断前的状态继续运行。

                Linux怎么做的:其实和单片机大同小异,详情见文章:Linux异常处理框架。重点是异常处理框架到asm_do_IRQ就进入具体的中断处理了。那么怎么问题来了,Linux中一个中断发生了,比如调用了open函数(软中断),Linux是怎么知道的?怎么也要有PIC的初始化和中断源的相关配置吧,并且,asm_do_IRQ。。。最终执行的是我们自己的程序,他是怎么知道的?难道不需要去告知Linux一下吗?

二、asm_do_IRQ分析(带着以上问题)

注意这里asm_do_IRQ是带参数的C函数,具体的参数是:r0 = irq(中断源编号)  r1 = 一个reg的地址(应该是sp的地址吧)

再往下看:irq.c

再往下看:irq.c

核心的generic_handle_irq:

到这里,中断处理的框架似乎就分析完了,但仍然不能解决我的困惑,一个新问题:desc->handle_irq是什么?它似乎指向了我们BSP定义的处理函数。

三、Linux的IRQ初始化

        在Linux中有对IRQ的初始化,完成desc项的填充,下面来看看:

        在start_kernel(main.c):

        

        先看early_irq_init():注意这里是有分支选择

        

        以上主要就是定义desc数组和填充 一些提前能填充的项吧,各种info。

        下面看重点的init_IRQ():

        

        在老一点的内核这个函数直接就是做主体工作了,然而在Linux-3.4.2中又封装了一层。。。。找了很久才在单板相关的的mach-xxx.c中找到,如mach-mini2440.c:

        

        至于MACHINE_START的执行点在:start_kernel->setup_arch->执行。。。

        那么重点就在s3c24xx_init_irq函数了:在irq.c

        

/* s3c24xx_init_irq

* Initialise S3C2410 IRQ system

*/

void __init s3c24xx_init_irq(void)
{
    unsigned long pend;
    unsigned long last;
    int irqno;
    int i;

#ifdef CONFIG_FIQ

    init_FIQ();

#endif

    irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");

    /* first, clear all interrupts pending... */

    last = 0;

    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C24XX_EINTPEND);
        if (pend == 0 || pend == last)
            break;
        __raw_writel(pend, S3C24XX_EINTPEND);
        printk("irq: clearing pending ext status %08x\n", (int)pend);

        last = pend;
    }

    last = 0;

    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_INTPND);
        if (pend == 0 || pend == last)
            break;
        __raw_writel(pend, S3C2410_SRCPND);
        __raw_writel(pend, S3C2410_INTPND);
        printk("irq: clearing pending status %08x\n", (int)pend);
        last = pend;
    }

    last = 0;

    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_SUBSRCPND);
        if (pend == 0 || pend == last)
            break;
        printk("irq: clearing subpending status %08x\n", (int)pend);
        __raw_writel(pend, S3C2410_SUBSRCPND);
        last = pend;
    }

    /* register the main interrupts */
    irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");

    for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
        /* set all the s3c2410 internal irqs */
        switch (irqno) {
            /* deal with the special IRQs (cascaded) */
        case IRQ_EINT4t7:
        case IRQ_EINT8t23:
        case IRQ_UART0:
        case IRQ_UART1:
        case IRQ_UART2:
        case IRQ_ADCPARENT:
            irq_set_chip_and_handler(irqno, &s3c_irq_level_chip,
                         handle_level_irq);
            break;
        case IRQ_RESERVED6:
        case IRQ_RESERVED24:
            /* no IRQ here */
            break;
        default:
            //irqdbf("registering irq %d (s3c irq)\n", irqno);
            irq_set_chip_and_handler(irqno, &s3c_irq_chip,
                         handle_edge_irq);
            set_irq_flags(irqno, IRQF_VALID);
        }
    }

    /* setup the cascade irq handlers */
    irq_set_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
    irq_set_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
    irq_set_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
    irq_set_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
    irq_set_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
    irq_set_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

    /* external interrupts */以外部中断为例分析
    for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
        irqdbf("registering irq %d (ext int)\n", irqno);
        irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,//s3c_irq_eint0这是一个初始化的结构体
                     handle_edge_irq);//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);
        irq_set_chip_and_handler(irqno, &s3c_irqext_chip,
                     handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    /* register the uart interrupts */
    irqdbf("s3c2410: registering external interrupts\n");

    for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {

        irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);

        irq_set_chip_and_handler(irqno, &s3c_irq_uart0,

                     handle_level_irq);

        set_irq_flags(irqno, IRQF_VALID);
    }

    for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {

        irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);

        irq_set_chip_and_handler(irqno, &s3c_irq_uart1,

                     handle_level_irq);

        set_irq_flags(irqno, IRQF_VALID);
    }

    for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {

        irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);

        irq_set_chip_and_handler(irqno, &s3c_irq_uart2,

                     handle_level_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {

        irqdbf("registering irq %d (s3c adc irq)\n", irqno);

        irq_set_chip_and_handler(irqno, &s3c_irq_adc, handle_edge_irq);

        set_irq_flags(irqno, IRQF_VALID);

    }

    irqdbf("s3c2410: registered interrupt handlers\n");

}

重点分析:

  irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,//s3c_irq_eint0这是一个初始化的结构体

                     handle_edge_irq);//handle_edge_irq是一个函数,和重要的

先看一下s3c_irq_eint0:

这些函数的作用实际就是中断控制器的初始化

那么handle_edge_irq呢?截取

再看handle_irq_event:

现在似乎明朗了不少,中断控制器的初始化(底层函数)在irq_chip结构体中定义;而自定义的处理函数以desc为索引,在action链表中。

所以需要一种注册机制,将这些结构体联系起来。

四、相关结构体

        第一,struct irq_desc

        第二,struct irq_chip

        第三,action结构体链表

五、注册和注销

   request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);

    void free_irq(unsigned int irq, void *dev_id)

六、Linux中断的顶半部和低半部机制(重点)

    理论上,中断做的是紧急的事,但是不能长时间占用,因为CPU不是为你一家开的,还有很多任务等着调度呢!

    那么Linux中断就引入了顶半部和低半部机制:

            在使用request_irq注册的handler就是顶半部函数,会屏蔽中断,做一些特别紧急,且开销很小的事,如设置flag,读写IO寄存器等;在 request_irq里面调度一个底半部的函数去做几乎中断需要做的所有事,且中断是打开的,可以让顶半部继续接收中断,处理更多的中断事件,或者调度其他的任务。

            在低半部机制机制中主要有:tasklet、工作队列、软中断和线程。应用都比较简单。

猜你喜欢

转载自blog.csdn.net/Carl_0/article/details/87855802