中断和时钟技术可以提升驱动程序的效率
中断
中断在Linux中的实现
通常情况下,一个驱动程序只需要申请中断,并添加中断处理函数就可以了,中断的到达和中断函数的调用都是内核实现框架完成的。所以程序员只要保证申请了正确的中断号及编写了正确的中断处理函数即可。
中断的宏观分类
1.硬中断
由系统硬件产生的中断。系统硬件通常引起外部事件。外部事件事件具有随机性和突发性,因此硬件中断也具有随机性和突发性。
2.软中断
软中断是执行中断指令时产生的。软中断不用外设施加中断请求信号,因此软中断的发生不是随机的是程序安排好的。汇编程序中软中断指令int n,n必须是中断向量。
处理器接收软中断有两个来源:(1)处理器执行到错误的指令代码;(2)软件产生中断,如进程的调度就是使用的软中断。
中断产生的位置分类
1.外部中断
外部中断一般是由计算机外设发出的中断请求,如键盘中断、定时器中断等。外部中断是可以通过编程方式给予屏蔽的。
2.内部中断
内部中断指因硬件出错(如突然断电、奇偶校验错等)或运算出错(除数为0、运算溢出、单步中断等)所引起的中断。内部中断是不可屏蔽的中断。通常情况下,大多数内部中断由Linux内核进行了处理,所以驱动程序员不需要关心这些问题。
同步和异步中断
1.同步中断
同步中断是指令执行的过程中由CPU控制的,CPU在执行完一条指令后才发出中断。也就是说,在指令执行的过程中,即时有中断的到来,只要指令还没执行完,CPU就不会去执行该中断。同步中断一般是因为程序错误所引起的,例如内存管理中的缺页中断,被0除出错等。当CPU决定处理同步中断时,会调用异常处理函数,使系统从错误的状态恢复过来。当错误不可恢复时,就会死机会蓝屏。
2.异步中断
异步中断是由硬件设备随机产生的,产生中断时并不考虑与处理器时钟同步问题,该类型的中断时可以随时产生。例如在网卡驱动程序中,当网卡接收到数据包后,会向CPU发送一个异步中断事件,表示数据到来,CPU并不知道何时将接收该事件。异步中断的中断处理函数与内核的执行顺序是异步执行的,两者没有必然的联系,也不会互相影响。
中断的实现过程
中断的实现过程较为复杂,涉及中断信号线、中断控制器等概念。
1.中断信号线(IRQ)
中断信号线是对中断输入线和中断输出线的统称。中断输入线是指接收中断信息的引脚。中断输出线是指发送中断信息的引脚。每一个能够产生中断的外设都有一条或者多条中断输出线(简称IRQ),用来通知处理器产生中断。
2.中断控制器
中断控制器位于ARM处理器核心和中断源之间。外部中断源将中断发送到中断控制器。中断控制器根据优先级进行判断,然后通过引脚将中断请求发送给ARM处理器核心。
当外部中断同时产生中断时,中断优先级产生逻辑会判断哪一个中断将被执行。中断屏蔽寄存器:当 屏蔽位为0表示对应的中断可以正常执行,否则对应的中断被禁止。
中断处理过程
中断的安装与释放
当设备需要中断功能时,应该安装中断。如果驱动程序员没有通过安装中断的方式通知Linux内核需要使用中断,那么内核只会简单的应答并且忽略该中断。
1.申请中断线
申请中断线可以使内核指导外设应该使用哪一个中断号,哪一个中断处理函数。
函数实现:kernel/irq/Manage.c
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) { return request_threaded_irq(irq, handler, NULL, flags, name, dev); }
2.释放中断线 kernel/irq/Manage.c
void free_irq(unsigned int irq, void *dev_id) { kfree(__free_irq(irq, dev_id)); }
按键中断实例
时钟机制
Linux驱动程序中经常会使用一些时钟机制,主要是用来延时一段时间。在这段时间内硬件设备可以完成相应的工作。
时间度量
一个与时钟中断相关的全局变量HZ。时钟中断是由系统定时硬件以周期性的间隔产生,这个周期性的值由HZ来表示。
HZ一般被定义为1000,表示一秒钟时钟中断发生1000次。每当时钟中断发生时,内核内部计数器的值就会加上1。内部计数器由jiffies变量表示,当系统初始化时,这个变量被设置为0。
时间延时
C语言中,经常使用sleep()函数将程序延时一段时间,这个函数能够实现毫秒级的延时。在设备驱动程序中,很多对设备的操作也需要延时一段时间,使设备完成某些特定的任务。
Linux内核中,两种常见的延时技术
1.短时延时
当设备驱动程序需要等待硬件处理的完成时,会主动地延时一段时间。这个时间一般是几十毫秒,甚至更短的时间。例如,驱动程序向设备的某个寄存器写入数据时,由于寄存器的写入速度较慢,所以需要驱动程序等待一定的时间,然后继续执行下面的工作。
static inline void ndelay(unsigned long x); static inline void udelay(unsigned long usecs); static inline void msleep(unsigned int msecs);
2.长时延时
长时延时实现一般是比较当前jiffies和目标jiffies的值。长时延时可以使用忙等待来实现。
延时3秒钟的实例:
unsigned long timeout = jiffies + 3*HZ; while(time_before(jiffies, timeout));
time_before宏简单的比较两个时间值得大小。如果参数1<参数2,返回true。
To be continue...