1、第一部分 中断处理程序(异步执行) :
1)任务特性:时间敏感、硬件相关、需要屏蔽其它中断。一般完成通过硬件确认中断、从硬件拷贝数据等功能。
硬件产生中断电信号,中断处理器发送至处理器——>处理器接收并向操作系统反应——>操作系统处理数据
与异常差别:异常(同步中断)产生必须与处理器时钟同步,由软件引起。但是两者的处理方式相似。
2)中断处理程序(中断服务例程,ISR)<linux/interrupt.h>
C函数,被内核调用来响应中断,是管理硬件的驱动程序的组成部分。运行在中断上下文中(又称原子上下文),该上下文中执行的代码不可阻塞。
上半部:主要负责通知硬件设备中断已经被接收,以及将硬件数据拷贝到内存,将处理交给协议栈或者应用程序。
注册:驱动程序通过request_irq(IRQ,handler,flags,name,dev)注册一个中断处理程序,得到中断线。
参数:irq 分配的中断号;handler 指向实际的中断处理程序
flags 标志; name 与中断相关设备的文本表示; dev 用于共享中断线
释放中断处理函数:free(irg,dev)
编写中断处理函数 irqreturn_t handler(irq,dev) 返回至:IRQ_NONE,IRQ_HANDLED
共享中断处理函数需要设置标志位IRQF_SHARED,dev参数也必须唯一指定,以及硬件支持。
3)中断上下文
由于没有后备进程,中断上下文不能睡眠,因此不能从中断上下文调用可能睡眠的函数。
中断处理程序打断其它程序执行,因此应该迅速简洁。
中断处理程序栈是可配置选项,大小为一页。
4)实现机制
设备产生中断——总线(电信号)——>中断控制器(中断线处于激活状态)——中断(电信号)——>处理器没有禁止中断,关闭中断系统跳入内核预定义位置(每条中断线对应一个位置)执行代码。初始入口点在栈中存放IRQ号并存放当前寄存器的值,然后内核调用do_IRQ()。do_IRQ()应答所中断,禁止线上的中断传递。
5)proc/interrupt
procfs虚拟文件系统,存在于内核内存,安装在proc目录。通过内核函数读写文件,模拟真实文件读写。
例子:/proc/interrupts文件存放系统中与中断相关的统计信息。
6)中断控制
与体系结构相关,用于操作中断的接口,在<asm/system.h><asm/system.h>
禁止和激活当前处理器本地中断 local_irq_enable/disable; 禁止中断以及恢复到原来状态:local_irq_save/restore 保存中断前系统状态
禁止特定中断线:禁止给定中断向所有处理器传递 / 中断函数返回前中断线上没有处理程序
中断系统的状态:当前系统处于中断禁止或者激活状态irqs_disable(<asm/system.h>),检查内核当前上下文接口 <linux/hardirq.h>
2、第二部分 下半部
实现机制:
1)软中断 由softirq_action结构表示,定义于<linux/interrupt.h>,每个被注册的软中断都为kernel/softirq.c中数组中一项。
软中断处理程序:执行acton函数;执行:注册软中断——>被标记后触发,在do_softirq()中执行。中断处理程序在返回前标记软中断,使其稍后被执行。
使用:<linux/interrupt.h>静态声明软中断——>注册软中断处理程序open_softirq(索引号,函数)——>触发 raise_softirq()将软中断设置为挂起状态,调用do_softirq运行
2)tasklets
利用软中断实现的下半部机制。软中断一般用于执行频率很高和连续性要求很高的情况。
由tasklet_struct结构表示,包括链表指针、状态、处理函数、参数等。
由两类优先级不同的软中断实现:HI_SOFTIRQ,TASKLET_SOFTIRQ。分别由tasklet_schedule()和tasklet_hi_schedule()函数调度。
调度过程:检查链表中的tasklet状态——>调用_tasklet_schedule()——>保存中断状态,禁止本地状态——>将需要调度的tasklet加到处理器tasklet_vec链表表头——>唤起TASKLET_SOFTIRQ或HI_SOFTIRQ软中断——>恢复中断到原状态返回。
使用:声明tasklet(动态或者静态方式)——>编写tasklet处理程序tasklet_handler(data) 使用软中断实现——>调用tasklet_schedule(&my_tasklet),可以禁止、激活、去掉
是否及时处理所有软中断或者给用户执行事件的折中:当大量软中断出现——>内核唤醒处于最低优先级的内核线程处理负载 kesofirqd/n。
3)工作队列
推后工作,交由内核线程执行,在进程上下文执行,允许重新调度、睡眠。
实现:有创建内核线程的接口,创建的工作者线程负责执行队列中的任务。可以由驱动程序创建专门工作者线程处理工作,或者由缺省工作者线程events/n。
工作线程的数据结构:workqueue_struct,包含cpu_workqueue结构体。执行worker_thread()函数,循环&被唤醒&休眠。
工作的数据结构:<linux/workqueue.h>中的work_struct表示,连成链表供工作者线程执行。
使用工作队列:
I 创建工作 DECLARE_WORK(name,fun,data)。
II 工作队列处理函数 work_handler(data),由工作现场执行。
III 调度 。把给定的工作函数work给缺省events,调用schedule_work(&work)
IV 刷新操作。调用下面的函数或者防止竞争条件。
V 创建新的工作队列与之相应的工作者线程。workqueue_struct *create_workqueue(name)
3、三种机制的选择:
软中断:序列化保障少,性能最高。必须静态创建,三者中较难使用的。上下文是中断。
tasklet:不能并发运行的软中断。上下文是中断。
任务队列:把任务推后到进程上下文中完成。开销最大,易于使用。
下半部禁止: 一般有进程共享时,需要先得到锁再禁止下半部的处理。local_bh_enable(disable) 与硬件体系结构相关,能禁止异步的软中断和tasklet,位于<asm/softirq.h>