linux中注册中断历程用request_irq,isr原型为: irqreturn_t *(int irq, void* dev_id, struct pt_reg* regs)
为了使中断关闭的时间尽可能的短,linux提出了中断上半部和下半部。上半部为request_irq注册的ISR,要求时间尽可能的短,而将尽可能的工作推迟到下半部去做。下半部由上半部调度,并在安全的时间内执行,此时中断已经打开了。linux有两种机制可以实现下半部:tasket和workqueue。
tasklet为软中断的一种。linux内核有HI_SOFTIRQ, TIMER, NET_TX, NET_RX, BLOCK, TASKLET, SCHED, HRTIMER, RCU 等软中断, 在系统初始化时调用open_softirq注册。在isr中声明tasklet,然后函数tasklet_schedule()调用raise_softirq(TASKLET_IRQ),然后系统在合适的时间调度tasklet运行。
workqueue本质为内核线程。
create_workqueue -> create_workqueue_thread
|| |->kthread_create(worker_thread) // worker_thread 检查work_list,如果非空则依次执行起func
|| |->设置kthread_create_list,然后唤醒kthreadd_task(负责检查kthread_create_list,如果非空则调用kernel_thread创建内核线程)
|| -> start_workqueue_thread
||-> wake_up_process(kthread_create返回的worker_thread) //通过设置task->state=RUNNING使得下次可以调度到。
schedule_work = queue_work(eventd_wq, work) // eventd_wq 为系统初始化时创建的workqueue。用户可以用create_workqueue创建自己的workqueue。
tasklet中不允许休眠,而workqueue允许。tasklet调用时为中断上下文,workque为进程上下文(仅有内核代码)。
linux 中的timer 即采用了中断和softirq的机制实现的。在时钟中断例程 timer_interrupt中调用do_timer_interrupt,更新jiffies,检查进程时间片,设置TIMER_SOFTIRQ并调用timer的软中断(在这里处理与timer有关的tvec_base),最后统计信息。