目录
3.ISR---interrupt service routine(中断服务函数)
6.3 cancel_delayed_work_sync()
一、基本概念
1.中断
由于I/O操作的不确定因素以及处理器和I/O设备之间的速度不匹配(异常事件),I/O设备可以通过某种硬件信号异步唤醒对应的处理器的响应,这些硬件信号叫做中断。
2.IRQ---interrupt request
Linux内核为每个中断线分配一个唯一的中断编号(IRQ)(可以使用gpio_to_irq函数,获取到对应的中断编号),也称为中断请求号。不同的计算机系统分配方式不同的。
3.ISR---interrupt service routine(中断服务函数)
系统响应外设的中断处理是通过外设所在的中断线上绑定的中断服务例程(ISR)实现的,也成为中断处理函数、中断服务函数等。
注意:一个中断线可以绑定一个或多个中断服务例程。多个中断线可以绑定同一个中断服务例程。
关键点注意:一个外设只能对应一个中断服务例程。
4.中断工作机制
当外设发出中断请求时(按键),会通过所在的中断线传递给linux系统,处理器会停止当前的工作,保存断点,然后跳到该中断线所绑定的中断服务函数(ISR),执行相应的中断服务函数。中断服务处理完毕后,处理器再返回到断点继续执行原来的工作。
5.共享中断和重用中断服务函数的区别
(1)挂载不同中断线上的不同外设,通常使用不同的中断服务例程,无需通过设备参数进行区分。(各个外设中断不同)
如果这些外设中断服务处理的框架是相同(例如:key1 key2),则可以使用同一个中断服务例程,通过设备参数来区分不同的外设。这个情况不是共享中断,是重用中断服务例程。
(2)挂载同一个中断线上的不同外设,使用同一个中断服务例程,必须参数来区分不同的外设。此时称为共享中断。(很灵活)
6.中断顶半部和中断底半部
Linux内核将中断分为顶半部和低半部机制,中断服务例程属于顶半部(核心的事情,不耗费时间),在中断服务例程(顶半部)结束之前可以启动中断底半部机制,将耗时的事务交给中断低半部处理。
作用: 顶半部通常会立即执行--不允许被打断,时效性更高。
而底半部是尽快执行,不能保证立即执行,而是内核择机调度,有机会就执行。
总结:常用的底半部机制包括:softirq、tasklet、工作队列workqueue
特点:softirq、tasklet都运行于中断上下文 工作队列运行于进程上下文(tasklet比softirq更好用一些)。
内核定时器timer作为定时机制,可以用于中断低半部,实现精准的延时功能。(类似:M4端的定时器)
二、Linux中断顶半部相关的API函数和宏
1.request_irq()
头文件:
#include <linux/interrupt.h>
原型:
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev_id)
功能:
注册中断,绑定对应的中断服务函数,并指明中断的中断编号,中断属性,设备参数
参数:
Irq:指定的中断编号。
handler:中断服务函数
flags:中断属性 指的是触发条件 上升沿或则下降沿
name:自定义的属于该中断的设备名 作用:内核会根据这个名字创建/proc/irq/中断编号/name/ 目录spurious这个文件会记录中断发生的次数。
dev_id:挂在该中断线上的设备的标识符。 共享中断的时候是不能省的。
返回值:
成功 0
失败:负数错误码 -EBUSY表示中断编号已经被占用
源码的基本形式:
static inline int __must_check
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.gpio_to_irq()
头文件:
#include <linux/gpio.h>
原型:
Int gpio_to_irq(unsigned gpio)
功能:
获取指定的GPIO引脚编号的中断编号
参数:
GPIO:GPIO引脚编号
返回值:
成功>0 表示对应的中断编号
失败 <0
3.free_irq()
头文件:
#include <linux/interrupt.h>
原型:
void free_irq(unsigned int irq,void *dev_id) //一般在退出函数里面会使用
功能:
注销指定中断线的指定的设备,取消该设备触发中断
参数:
Irq:代表指定中断线上的中断编号
dev_id:挂在该中断线上的设备的标识信息,必须与request_irq保持一致
返回值:
无
4.disable_irq()
头文件:
#include <linux/interrupt.h>
原型:
void disable_irq(unsigned int irq)
功能:
禁止指定的中断线 注意:禁用处理器上的中断线,会阻塞等待该中断线上的中断服务例程,所以不能用于中断上下文。
参数:
Irq: 表示中断线上的中断编号
返回值:
无
内核里面的API函数的原型:
/**
* 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)
{
if (!__disable_irq_nosync(irq))
synchronize_irq(irq);
}
5.disable_irq_nosync()
头文件:
#include <linux/interrupt.h>
原型:
void disable_irq_nosync(unsigned int irq) --退出函数里面
功能:
禁用一次中断线,不会等待该中断线上的中断服务例程,可以用于中断上下文
参数:
Irq:表示中断线上的中断编号
返回值:
无
内核源码的基本形式
/**
* disable_irq_nosync - disable an irq without waiting
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Disables and Enables are
* nested.
* Unlike disable_irq(), this function does not ensure existing
* instances of the IRQ handler have completed before returning.
*
* This function may be called from IRQ context.
*/
void disable_irq_nosync(unsigned int irq)
{
__disable_irq_nosync(irq);
}
6.enable_irq()
头文件:
#include <linux/interrupt.h>
原型:
void enable_irq(unsigned int irq)
功能:
使能处理器上的中断线
参数:
Irq:表示中断线上的中断编号
返回值:
无
三、系统定时器
Linux系统定时器有硬件定时器,可以设置的频率,频率:1秒内系统定时触发中断的次数,HZ表示。
如果频率越高,定时时间间隔越小,导致开销和电源的消耗越高。(如果要省电的话,需要降低频率)
注意:linux系统定时器的计时单位jiffy, 可以理解为:触发中断的周期时长,1s=1jiffy+ 1*HZ,每个滴答之后,jiffy就会加1
1.msecs_to_jiffies()
头文件:
#include <linux/jiffies.h>
原型:
unsigned long msecs_to_jiffies(const unsigned int m)
功能:
将以毫秒为单位的时长转换为以jiffy为单位的时长
参数:
m:毫秒数
返回值:
相同时长的jiffy的个数
内核源码的基本形式:
static __always_inline unsigned long msecs_to_jiffies(const unsigned int m)
{
if (__builtin_constant_p(m)) {
if ((int)m < 0)
return MAX_JIFFY_OFFSET;
return _msecs_to_jiffies(m);
} else {
return __msecs_to_jiffies(m);
}
}
2.usecs_to_jiffies()
头文件:
#include <linux/jiffies.h>
原型:
unsigned long usecs_to_jiffies(const unsigned int m)
功能:
将微