中断处理之二: Top half & Bottom half

中断处理函数(interrupt handler)有一个要求就是要短小精悍,运行时间越短越好。因为在中断处理函数之前,会先关中断,那么在中断处理函数执行的这段时间内,所有发生的中断将都不能被处理。但是很多时候我们在中断处理中必须要做很多事情,这时候怎么办呢?

内核里面提供了bottom half这样的机制使得我们可以达到这样的目的。

Top half & Bottom half
中断处理过程被分成了两个部分,top half会将所需要作的工作全部交给bottom half去做然后自己立刻返回。在内核中提供了下面三种机制可以让我们实现bottom half: softirqs, tasklets 和 work queues.


Softirq是基础的bottom half机制, tasklet是建立在softirq之上的。这二者的主要区别在于Softirq是可重入的,而tasklet是不可重入的。

以下是示例代码

Softirq示例代码:

void __init softirq_sample_init()
{
    /* … */
   
    /*调用open_softirq打开自定义的SAMPLE_SOFT_IRQ软中断*/

    open_softirq(SAMPLE_SOFT_IRQ, softirq_sample_bottom, NULL);

    /*SAMPLE_SOFT_IRQ这个东西要先自己手动加到
        include/linux/interrupt.h中的那个enum中去*/

    /*这里参数的第二项就是bottom half的执行函数*/
}

/*Bottom half*/
void softirq_sample_bottom()
{
    /*做bottom half中该做的事情*/
}

/*Interrupt handler*/
static irqreturn_t softirq_sample_interrupt(int irq, void *dev_id)
{
    /* … */

    raise_softirq(SAMPLE_SOFT_IRQ);
    /*这样,就会让bottom half开始执行*/

    return IRQ_HANDLED;
}

Tasklets示例代码:

sturct tasklet_sample_device_struct { /* Pre-device structure */
    /* … */
    struct tasklet_struct tasklt;
    /* … */
}

void __init tasklet_sample_init()
{
    struct tasklet_sample_device_struct *dev_struct;
    /* … */

    /*初始化tasklet*/
    tasklet_init(&dev_struct -> tasklt, tasklet_sample_bottom, dev);
}

/* Bottom half */
void tasklet_sample_bottom()
{
    /* 做bottom half中该做的事情 */
}

/* Interrupt handler*/
static irqreturn_t tasklet_sample_interrupt(int irq, void *dev_id)
{
    struct tasklet_sample_device_struct *dev_struct;
   
    /* … */
   
    tasklet_schedule(&dev_struct -> tasklt);

    return IRQ_HANDLED;
}