linux中断控制之总章

来源:互联网 发布:校园网网络波动大 编辑:程序博客网 时间:2024/04/28 23:19
程序运行期间,遇到某些特殊情况,需要CPU暂停当前正在执行的程序,转去处理突发事件,处理完后又返回原程序被中断的位置继续运行,这种情况就是中断。
中断源:引起中断的原因或发出中断请求的设备
中断服务程序:要处理的突发事件程序

中断的分类
1.来源
    硬中断(外部中断):通过外部设备接口,向CPU的中断请求引脚INT和NMI发中断请求产生
    软中断(内部中断):CPU内部执行中断指令,或由运算溢出,TF(Trap Falg,每执行一条指令,自动产生一个内部中断去执行一个中断服务程序)标志而产生
2.可否能屏蔽
    可屏蔽中断(INT)
    不可屏蔽中断(NMI)
3.入口跳转
    向量中断:不同的中断分配不同的中断号,有不同的入口地址,硬件提供
    非向量中断:多个中断共享一个入口地址,再通过中断标志识别具体哪个中断,软件提供

中断程序架构
linux中断程序不一定要分为顶半部(tophalf)和底半部(bottom half),通常为什么要分呢?
因为中断处理程序是在关掉其他所有中断的情况下进行,执行于硬件相关的处理要求快,而有些驱动在中断处理程序中又需要完成大量的工作,这就矛盾了。这需要在这两者间找到一个平衡点,所以分解为两个部分。

顶半部的功能是“登记中断”。顶半部尽可能快的完成比较急的功能,往往只是简单的读取寄存器中的中断状态并清除中断标志后进行“登记中断”即中断例程的底半部挂到该设备的底半部执行队列中去。这样顶半部执行的速度很快,能服务更多的中断请求。
底半部来完成中断事件的绝大多数使命。
顶半部与底半部最大的不同是,底半部是可中断的,顶半部不可中断。

顶半部机制
申请和释放中断函数request_irq() 和 free_irq() 
 
int request_irq(unsigned int irq, 
irq_handler_t handler,
unsigned long irqflags, 
const char * devname, 
void *dev_id); 
irq是要申请的硬件中断号。handler是向系统登记的中断处理函数。这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,device id,寄存器值。dev_id就是下面的request_irq时传递给系统的参数dev_id。irqflags是中断处理的一些属性。比较重要的有标明中断处理程序是快速处理程序(设置IRQF_DISABLED)还是慢速处理程序(不设置IRQF_DISABLED)。快速处理程序被调用时屏蔽所有中断。慢速处理程序不屏蔽。还有一个IRQF_SHARED属性,设置了以后运行多个设备共享中断。dev_id在中断共享时会用到。一般设置为这个设备的 device结构本身或者NULL。
中断处理程序可以用dev_id找到相应的控制这个中断的设备,或者用irq2dev_map找到中断对应的设备。 
void free_irq(unsigned int irq,void *dev_id); 

使能和屏蔽中断
void disable_irq(int irq)
void disable_irq_nosync(int irq)
void enable_irq(int irq)
disable_irq()和disable_irq_nosync()的区别在于,后者立即返回,而且前者等待目前的中断处理完成。
如果enable_irq()函数会引起系统死锁,这种情况下,只能使用disable_irq_nosync()

local_irq_save(flags)
local_irq_restore(flags)
注意:保存的是数值,不是指针,因为flags是unsigned long类型,所以这对中断函数要在同一个函数中使用

底半部机制
linux实现底半部的机制主要有软中断,工作队列,tasklet。

软中断: 
    1.软中断是在编译期间静态分配的
    2.最多可以有32个软中断
    3.软中断不会抢占另外一个软中断,唯一可以抢占软中断的是中断处理程序
    4.可以并发运行在多个CPU上(即使同一类型的也可以)。所以软中断必须设计为可重入的函数(允许多个CPU同时操作),因此也需要使用自旋锁来保护其数据结构
    5.目前只有两个子系直接使用软中断:网络和SCSI
    6.执行时间有:从硬件中断代码返回时、在ksoftirqd内核线程中和某些显示检查并执行软中断的代码中

tasklet: 
    1.tasklet是使用两类软中断实现的:HI_SOFTIRQ和TASKLET_SOFTIRQ
    2.可以动态增加减少,没有数量限制
    3.同一类tasklet不能并发执行
    4.不同类型可以并发执行
    5.大部分情况使用tasklet

工作队列:
    1.由内核线程去执行,换句话说总在进程上下文执行
    2.可以睡眠,阻塞

tasklet会在很短的时间内很快执行,并且以原子模式执行,而工作队列函数可以具有更长的延迟并且不必原子化

用例
void my_tasklet_func(unsigned long);     //定义一个处理函数:
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);     //定义一个tasklet结构my_tasklet,与my_tasklet_func(data)函数相关联
 ……
tasklet_schedule(&my_tasklet);    //调度tasklet


在linux中,可以查看系统中中断的统计信息
[wang@localhost kernel]$ cat /proc/interrupts 
           CPU0       
  0:       1238   IO-APIC-edge      timer
  1:      76229   IO-APIC-edge      i8042
  3:          8   IO-APIC-edge    
  4:     115306   IO-APIC-edge    
  7:          0   IO-APIC-edge      parport0
  8:         20   IO-APIC-edge      rtc0
  9:          0   IO-APIC-fasteoi   acpi
 12:     696238   IO-APIC-edge      i8042
 14:          0   IO-APIC-edge      ata_piix
 15:    2532908   IO-APIC-edge      ata_piix
 16:     348132   IO-APIC-fasteoi   Ensoniq AudioPCI
 17:     359979   IO-APIC-fasteoi   ehci_hcd:usb1, ioc0
 18:        248   IO-APIC-fasteoi   uhci_hcd:usb2
 19:       2183   IO-APIC-fasteoi   vmxnet ether
……
NMI:          0   Non-maskable interrupts
LOC:   20587881   Local timer interrupts
RES:          0   Rescheduling interrupts
CAL:          0   Function call interrupts
TLB:          0   TLB shootdowns
TRM:          0   Thermal event interrupts
SPU:          0   Spurious interrupts
ERR:          0
MIS:          0


 
原创粉丝点击