linux中断的顶半部和底半部


linux中断底部和顶部的由来:
在大多数真实的系统中,当中断到来时,要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。 
为了在中断执行时间尽可能短和中断处理需完成大量工作之间找到一个平衡点,Linux 将中断处理程序分解为两个半部:顶半部(top  half)和底半部(bottom half)。 中断处理例程应该尽可能的短,执行一个长时间的任务时最好的方法是使用底半部机制。
Linux内核中断机制:为了在中断执行时间尽可能短和中断处理需要完成大量工作之间找到一个平衡点,Linux将中断处理程序分解为两个半部,顶半部和底半部。顶半部完成尽可能少的比较紧急的任务,它往往只是简单地读取寄存器中的中断状态并清除中断标志位就进行“登记工作”,将底半部处理程序挂到该设备的底半部执行队列中去


Linux实现下半部的机制主要有tasklet和工作队列。



一:tasklet


记住tasklet是一个可以在由系统决定的安全时刻在软件中断上下文被调度运行的特殊函数,它们可以被多次调度运行,但tasklet不会积累,也就是说,实际只会运行一次。不会有同一个tasklet的多个实例并行的运行,因为它们只运行一次,但是tasklet可以与其它的tasklet并行的运行在对称多处理器(SMP)系统上。如果驱动程序中有多个tasklet,它们必须使用某种机制锁来避免冲突。tasklet在中断处理例程结束前不会开始运行,tasklet运行时,可以有其它的中断发生。tasklet通常是底半部处理的优选机制,因为这种机制非常快,但是所有的tasklet都必须是原子的。tasklet使用相当简单,我们只需要定义tasklet及其处理函数并将二者关联:


 void my_tasklet_func(unsigned long); //定义一个处理函数:
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data); //定义一个tasklet结构my_tasklet,与
my_tasklet_func(data)函数相关联 。data为传入处理函数的参数。然后,在需要调度tasklet的时候引用一个简单的API就能使系统在适当的时候进行调度运行: tasklet_schedule(&my_tasklet);


实例: 

//定义与绑定tasklet函数
void test_tasklet_action(unsigned long t);
DECLARE_TASKLET(test_tasklet, test_tasklet_action, 0);
//中断处理底半部
 void test_tasklet_action(unsigned long t)
{

printk("tasklet is executing\n");

}

/*中断处理顶半部*/
static irqreturn_t xxx_interrupt(int irq, void *dev_id)


{
  .....


    tasklet_schedule(&test_tasklet);


    .....
}


/*设备驱动加载模块*/
int __init xxx_init(void)


{
      ......


    request_irq(IRQ_EINT0,xxx_interrupt, IRQ_TYPE_LEVEL_LOW, "xxx", NULL);


    ......


}




void __exit xxx_exit(void)
{


    ......


    free(IRQ_EINT0,NULL);


    ......


}




二:工作队列


工作队列会在将来某个时间,在某个特殊的工作者进程上下文中调用一个函数,因为工作队列函数运行在进程上下文中,因此可在必要时进行休眠,但是我们不能从工作队列向用户空间复制数据,要知道工作者进程无法访问其它任何进程的地址空间,除非使用进程间工享技术。
struct work_struct my_wq;/*定义一个工作队列*/
void my_wq_func(unsigned long);/*定义一个处理函数*/
INIT_WORK(&my_wq,(void (*)(void *))my_wq_func,NULL);/*初始化工作队列并
将其与处理函数绑定*/


示例:
struct work_struct my_wq;/*定义一个工作队列*/
void my_wq_func(unsigned long);/*定义一个处理函数*/
//中断处理底半部
void my_wq_func(unsigned long t)
{


    .......


}


/*中断处理顶半部*/
static irqreturn_t xxx_interrupt(int irq, void *dev_id)
{


    .....


    schedule_work(& my_wq);


    .....


}

/*设备驱动加载模块*/
int __init xxx_init(void)


{


    ......


    request_irq(IRQ_EINT0,xxx_interrupt, IRQ_TYPE_LEVEL_LOW, "xxx", NULL);
    INIT_WORK(&my_wq,(void (*)(void*))my_wq_func,NULL);/*初始化工作队列并


将其与处理函数绑定*/
  ......


}


/*设备驱动卸载模块*/
void __exit xxx_exit(void)


{
     ......


    free(IRQ_EINT0,NULL);


    ......


}

展开阅读全文

没有更多推荐了,返回首页