驱动: 中断【1】linux中断流程

通常情况下,当一个给定的中断处理程序正在执行时,所有其他的中断都是打开的,所以这些不同中断线上的其他中断都能被处理,但当前中断总是被禁止的。

将中断处理切为两个部分或两半。中断处理程序上半部(top half)---接收到一个中断,它就立即开始开始执行,但只做严格时限的工作,这些工作都是在所有中断被禁止的情况下完成的。同时,能够被允许稍后完成的工作推迟到下半部(bottom half)去,此后,下半部会被执行,通常情况下,下半部都会在中断处理程序返回时立即执行。

当执行一个中断处理程序或下半部时,内核处于中断上下文(interrupt context)中。

对比进程上下文,在进程上下文可以随时休眠,也可以调度程序。但中断上下文却完全不是这样,它不可以休眠。如果一个函数睡眠,就不能在中断处理程序中使用它。

此外,禁止中断还可以禁止内核抢占。然而,不管是禁止中断还是禁止内核抢占,都没有提供任何保护机制来防止来自其他处理器的并发访问。Linux支持多处理器,因此,内核代码一般都需要获取某种锁,防止来自其他处理器对共享数据的并发访问,获取这些锁的同时也伴随着禁止本地中断。锁提供保护机制,防止来自其他处理器的并发访问,而禁止中断提供保护机制,则是防止来自其他中断处理程序的并发访问。

 

下面是内核代码中的atmel-mci.c,正好使用了tasklet_schedule

static int __init atmci_init(void)
{
    return platform_driver_probe(&atmci_driver, atmci_probe);
}

static int __init atmci_probe(struct platform_device *pdev)
{
    struct atmel_mci        *host;
    regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    irq = platform_get_irq(pdev, 0); //platform_get_resource(dev, IORESOURCE_IRQ, num);
    ...
    spin_lock_init(&host->lock);
    tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
    ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
}

static void atmci_tasklet_func(unsigned long priv)
{
    spin_lock(&host->lock);
    do_sth();//
    spin_unlock(&host->lock);
}

static irqreturn_t atmci_interrupt(int irq, void *dev_id)
{
    ...
    tasklet_schedule(&host->tasklet);
    ...
}
//A tasklets can be concurrent with other tasklets, tasklet是参与调度的。
//Tasklets run in software interrupt context with the result that all tasklet code must be atomic. 
void __tasklet_schedule(struct tasklet_struct *t)
{
    unsigned long flags;
    /*
    local_irq_save和 local_irq_disable  http://www.360doc.com/content/14/0605/16/10249440_384022354.shtml

    在2.6内核中,可以通过下面两个函数中的其中任何一个关闭当前处理器上的所有中断处理,这两个函数定义在 <asm/system.h>中:
    void local_irq_save(unsigned long flags);
    void local_irq_disable(void);

    在2.6内核, 没有方法全局禁用整个系统的所有中断。 内核开发者认为关闭所有中断的代价太高,因此没有必要提供这个能力。
    */
    local_irq_save(flags);
    t->next = NULL;
    *__get_cpu_var(tasklet_vec).tail = t;
    __get_cpu_var(tasklet_vec).tail = &(t->next);
    raise_softirq_irqoff(TASKLET_SOFTIRQ);
    local_irq_restore(flags);
}

工作队列(workqueue) http://blog.chinaunix.net/uid-24148050-id-296982.html

  关于工作队列和tasklet的比较,可参考ldd3第7章。

linux中断流程详解 http://blog.chinaunix.net/uid-25622207-id-2897963.html

posted @ 2016-05-26 18:56  oucaijun  阅读(405)  评论(0编辑  收藏  举报
下载TeamViewer完整版 下载TeamViewer