中断
处理器的处理速度比硬件来说要快上N个数量级,那么由处理器向硬件发出请求并等待回应的办法显然是不可取的
硬件在需要的时候才向内核发出信号,然后处理器去响应硬件的请求。这就是中断机制
当硬件需要和处理器通信时,会产生一个电信号(即中断信号),并发往处理器
中断注册
#include <linux/irq.h> //irqflags
#include "arch/arm/mach-s3c2410/include/mach/irqs.h" //irq
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
irq:要申请的硬件中断号(cat /proc/interrupts 可查看)
handler:中断处理函数。中断发生时,回调这个函数,dev_id参数将被传递给它
irqflags:中断处理的属性。IRQF_DISABLED,则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断;IRQF_SHARED,则表示多个设备共享中断;IRQF_SAMPLE_RANDOM,表示对系统熵有贡献,对系统获取随机数有好处
devname:中断的名字(cat /proc/interrupts 可查看)
dev_id: 用于共享的中断信号线,必须唯一,可以用它来指向驱动程序的私有数据区(用来识别哪个设备产生中断);若中断没有被共享,可以设置为NULL
中断处理例程可在驱动程序初始化时或者第一次打开设备时进行安装,但为断信号线的数量是非常有限的,如果一个模块在初始化时请求了IRQ,那么即使驱动程序只是占用它而从未使用,也将阻止其它驱动使用该中断,而在打开设备时申请中断
中断处理例程是在中断时间内运行的。处理例程不能向用户空间发送或者接收数据,因为它不是任何进程的上下文中执行的,不能做任何可能发生休眠的操作,例如调用wait_event,使用不带GFP_ATOMIC标志的内存分配操作,或者锁住一个信号量等等,不能调用schdule函数
中断屏蔽
/**
* This function may be called from IRQ context.
*/
void disable_irq_nosync(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc)
return;
chip_bus_lock(irq, desc);
spin_lock_irqsave(&desc->lock, flags);
__disable_irq(desc, irq, false);
spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(irq, desc);
}
关闭中断后程序返回, 如果在中断处理程序中, 那么会继续将中断处理程序执行完
/**
* disable_irq - disable an irq and wait for completion
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Enables and Disables are
* nested.
* This function waits for any pending IRQ handlers for this interrupt
* to complete before returning. If you use this function while
* holding a resource the IRQ handler may need you will deadlock.
*
* This function may be called - with care - from IRQ context.
*/
void disable_irq(unsigned int irq)
{
struct irq_desc *desc = irq_desc + irq;
if (irq >= NR_IRQS)
return;
disable_irq_nosync(irq);
if (desc->action)
synchronize_irq(irq);
}
使能 IRQ
void enable_irq(int irq);
中断处理程序应当迅速、简洁,尽量把工作放到下半部中去完成
中断的下半部
中断处理程序中,只处理那些有严格时间限制的工作,比如复位硬件,对中断进行应答等。而那些可以拖到后面做的,或者说有可能睡眠的处理,都应当放到下半部去处理
下半部实现机制
软中断,tasklets和工作对列
- 软中断:软中断使用的并不多
- tasklets:通过软中断实现
- 工作队列:将工作推后给一个内核线程去执行。意味着可以进行睡眠,意味着当需要信号量、阻塞、大量内存时,工作队列应该是你的首选
工作队列和tasklet区别
- tasklet是通过软中断实现的,在软中断上下文中运行,tasklet代码必须是原子的;workqueue是通过内核进程实现的,工作队列函数可以休眠
- tasklet始终运行在被初始提交的同一处理器上,workqueue不一定
- tasklet不能确定延时时间(即使很短),workqueue可以设定延迟时间
workqueue API
struct workqueue_struct *create_workqueue(const char *name);
创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程
void destroy_workqueue(struct workqueue_struct *queue);
释放workqueue队列
int schedule_work(struct work_struct *work);
调度执行一个任务,执行的任务将会被挂入workqueue
int schedule_delayed_work(struct delayed_struct *work, unsigned long delay);
延迟一定时间去执行一个任务
queue_work(struct workqueue_struct *wq, struct work_struct *work);
调度执行一个指定workqueue中的任务
/proc/interrupts
# cat /proc/interrupts
CPU0 CPU1
0: 14678 0 IO-APIC-edge timer
1: 2 0 IO-APIC-edge i8042
4: 2 0 IO-APIC-edge
7: 0 0 IO-APIC-edge parport0
8: 1 0 IO-APIC-edge rtc0
9: 0 0 IO-APIC-fasteoi acpi
12: 4 0 IO-APIC-edge i8042
14: 45394223 0 IO-APIC-edge ata_piix
15: 0 0 IO-APIC-edge ata_piix
16: 56 16232636 IO-APIC-fasteoi i915, p2p1
18: 5333843 11365439 IO-APIC-fasteoi uhci_hcd:usb4
20: 2277759 0 IO-APIC-fasteoi ata_piix
从左到右分别是:中断号、在cpu上发生中断的次数、可编程中断控制器、设备名称
使用的中断的情况,如果中断处理没有安装,是不会显示的
详细的查看/proc/stat