Linux设备驱动中断机制
【主要内容】
Linux设备驱动编程中的中断与定时器处理
【正文】
一、基础知识
1、中断
所谓中断是指CPU在执行程序的过程中,出现了某些突发事件急待处理,CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU又返回程序被中断的位置并继续执行。
2、中断的分类
1)根据中断来源分为:内部中断和外部中断。内部中断来源于CPU内部(软中断指令、溢出、语法错误等),外部中断来自CPU外部,由设备提出请求。
2)根据是否可被屏蔽分为:可屏蔽中断和不可屏蔽中断(NMI),被屏蔽的中断将不会得到响应。
3)根据中断入口跳转方法分为:向量中断和非向量中断。向量中断为不同的中断分配不同的中断号,非向量中断多个中断共享一个中断号,在软件中判断具体是哪个中断(非向量中断由软件提供中断服务程序入口地址)。
二、Linux中断处理程序架构
设备的中断会打断内核中正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽可能的短小(时间短),但是在大多数实际使用中,要完成的工作都是复杂的,它可能需要进行大量的耗时工作。
1、Linux中断处理中的顶半部和底半部机制
由于中断服务程序的执行并不存在于进程上下文,因此,要求中断服务程序的时间尽可能的短。 为了在中断执行事件尽可能短和中断处理需完成大量耗时工作之间找到一个平衡点,Linux将中断处理分为两个部分:顶半部(top half)和底半部(bottom half)。
Linux中断处理机制
顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后进行“登记中断”的工作。“登记”意味着将底半部的处理程序挂载到该设备的底半部指向队列中去。底半部作为工作重心,完成中断事件的绝大多数任务。
a. 底半部可以被新的中断事件打断,这是和顶半部最大的不同,顶半部通常被设计成不可被打断
b. 底半部相对来说不是非常紧急的,而且相对比较耗时,不在硬件中断服务程序中执行。
c. 如果中断要处理的工作本身很少,所有的工作可在顶半部全部完成
三、中断编程
1、申请和释放中断
在Linux设备驱动中,使用中断的设备需要申请和释放相对应的中断,分别使用内核提供的 request_irq() 和 free_irq() 函数
a. 申请IRQ
typedef irqreturn_t (*irq_handler_t)(int irq, void *dev_id);
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
/* 参数:
** irq:要申请的硬件中断号
** handler:中断处理函数(顶半部)
** irqflags:触发方式及工作方式
** 触发:IRQF_TRIGGER_RISING 上升沿触发
** IRQF_TRIGGER_FALLING 下降沿触发
** IRQF_TRIGGER_HIGH 高电平触发
** IRQF_TRIGGER_LOW 低电平触发
** 工作:不写:快速中断(一个设备占用,且中断例程回调过程中会屏蔽中断)
** IRQF_SHARED:共享中断
** dev_id:在共享中断时会用到(中断注销与中断注册的此参数应保持一致)
** 返回值:成功返回 - 0 失败返回 - 负值(绝对值为错误码)
*/
b. 释放IRQ
void free_irq(unsigned int irq, void *dev_id);
/* 参数参见申请IRQ */
2、屏蔽和使能中断
void disable_irq(int irq); //屏蔽中短、立即返回
void disable_irq_nosync(int irq); //屏蔽中断、等待当前中断处理结束后返回
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void enable_irq(int irq); //使能中断
全局中断使能和屏蔽函数(或宏)
屏蔽:
#define local_irq_save(flags) ...
void local irq_disable(void );
使能:
#define local_irq_restore(flags) ...
void local_irq_enable(void);
3、底半部机制
Linux实现底半部机制的的主要方式有 Tasklet、工作队列和软中断
a. Tasklet
Tasklet使用简单,只需要定义tasklet及其处理函数并将二者关联即可,例如:
void my_tasklet_func(unsigned long); /* 定义一个处理函数 */
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
/* 定义一个名为 my_tasklet 的 struct tasklet 并将其与 my_tasklet_func 绑定,data为传入 my_tasklet_func的参数 */
只需要在顶半部中电泳 tasklet_schedule()函数就能使系统在适当的时候进行调度运行
tasklet_schedule(struct tasklet *xxx_tasklet);
tasklet使用模版
/* 定义 tasklet 和底半部函数并关联 */
void xxx_do_tasklet(unsigned long data);
DECLARE_TASKLET(xxx_tasklet, xxx_tasklet_func, data); /* 中断处理底半部 */
void xxx_tasklet_func()
{
/* 中断处理具体操作 */
} /* 中断处理顶半部 */
irqreturn xxx_interrupt(int irq, void *dev_id)
{
//do something
task_schedule(&xxx_tasklet);
//do something
return IRQ_HANDLED;
} /* 设备驱动模块 init */
int __init xxx_init(void)
{
...
/* 申请设备中断 */
result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED, "xxx", NULL);
...
return 0;
}
module_init(xxx_init); /* 设备驱动模块exit */
void __exit xxx_exit(void)
{
...
/* 释放中断 */
free_irq(xxx_irq, NULL);
}
module_exit(xxx_exit);
b. 工作队列 workqueue
工作队列与tasklet方法非常类似,使用一个结构体定义一个工作队列和一个底半部执行函数:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
struct work_struct my_wq; /* 定义一个工作队列 */
void my_wq_func(unsigned long); /*定义一个处理函数 */
通过INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定(一般在模块初始化中使用):
void INIT_WORK(struct work_struct *my_wq, work_func_t);
/* my_wq 工作队列地址
** work_func_t 处理函数
*/
与tasklet_schedule_work ()对应的用于调度工作队列执行的函数为schedule_work()
schedule_work(&my_wq);
工作队列使用模版
/* 定义工作队列和关联函数 */
struct work_struct xxx_wq;
void xxx_do_work(unsigned long); /* 中断处理底半部 */
void xxx_work(unsigned long)
{
/* do something */
} /* 中断处理顶半部 */
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
...
schedule_work(&xxx_wq);
...
return IRQ_HANDLED;
} /* 设备驱动模块 init */
int __init xxx_init(void)
{
...
/* 申请设备中断 */
result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED, "xxx", NULL);
/* 初始化工作队列 */
INIT_WORK(&xxx_wq, xxx_do_work);
...
return ;
}
module_init(xxx_init); /* 设备驱动模块exit */
void __exit xxx_exit(void)
{
...
/* 释放中断 */
free_irq(xxx_irq, NULL);
}
module_exit(xxx_exit);
c. 软中断
软中断(softirq)也是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候,tasklet的基于软中断实现的,因此也运行于软中断上下文。
在Linux内核中,用softirq_action结构体表征一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数。使用open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。
struct softirq_action
{
void (*action)(struct softirq_action *);
};
void open_softirq(int nr, void (*action)(struct softirq_action *)); /* 注册软中断 */
void raise_softirq(unsigned int nr); /* 触发软中断 */
local_bh_disable() 和 local_bh_enable() 是内核中用于禁止和使能软中断和tasklet底半部机制的函数。
Linux设备驱动中断机制的更多相关文章
- linux 设备驱动概述
linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer): 主要利用C库函数和 ...
- 浅谈Android系统移植、Linux设备驱动
一.Android系统架构 第一层:Linux内核 包括驱动程序,管理内存.进程.电源等资源的程序 第二层:C/C++代码库 包括Linux的.so文件以及嵌入到APK程序中的NDK代码 第三层:An ...
- linux设备驱动概述,王明学learn
linux设备驱动学习-1 本章节主要学习有操作系统的设备驱动和无操作系统设备驱动的区别,以及对操作系统和设备驱动关系的认识. 一.设备驱动的作用 对设备驱动最通俗的解释就是“驱使硬件设备行动” .设 ...
- Linux设备驱动工程师之路——内核链表的使用【转】
本文转载自:http://blog.csdn.net/forever_key/article/details/6798685 Linux设备驱动工程师之路——内核链表的使用 K-Style 转载请注明 ...
- linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-119723.html linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟 xxxxxxxxxx ...
- linux设备驱动归纳总结(十二):简单的数码相框【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-116926.html linux设备驱动归纳总结(十二):简单的数码相框 xxxxxxxxxxxxxx ...
- linux设备驱动归纳总结(十一):写个简单的看门狗驱动【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-112879.html linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxx ...
- linux设备驱动归纳总结(十):1.udev&misc【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-111839.html linux设备驱动归纳总结(十):1.udev&misc xxxxxxx ...
- linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxx ...
随机推荐
- 关于模板中的动态取值 ---反射与javascript脚本编译
在项目中经常遇到一个问题,打印word或者打印excel的时候,我们经常使用一对一的赋值或者批量替换的方式来对模板进行修改. 但是现在遇到两种场景: 1.取值是通过自定以方法进行取值的. 如:一个销售 ...
- [转]Delphi : keydown与keypress的区别,组合键
Shift 是一个集合变量. type TShiftState = set of (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDoubl ...
- centos node卸载
1.通过包管理工具 如果是通过包管理工具安装的话,那就和包管理工具卸载 yum remove nodejs npm -y 2.手动 如果是通过手动安装:官方下载后安装 进入到安装的路径 cd /opt ...
- POJ3320 Jessica's Reading Problem(尺取+map+set)
POJ3320 Jessica's Reading Problem set用来统计所有不重复的知识点的数,map用来维护区间[s,t]上每个知识点出现的次数,此题很好的体现了map的灵活应用 #inc ...
- 可以让PHP编程事半功倍的类库
在用php开发网站的时候,使用面向对象的方法确实可以提高代码复用率,减少代码冗余.而对初学者更友好的是,PHP开发网站所需要的大部分类库,网上都有十分优秀的类库存在了.作为一个程序猿当然不能重复制造轮 ...
- Java学习----设计正真的应用程序
import java.util.Scanner; // 输入10位学生的成绩,并且判断他们的成绩是哪个等级,其中90-100是A级,80-89是B级,70-79是C级,60-69是D级,60分以下E ...
- 百度上传插件WebUploader,angularjs指令封装
1.WebUploader特点 官网地址:http://fex.baidu.com/webuploader/ 1.1 分片.并发 分片与并发结合,将一个大文件分割成多块,并发上传,极大地提高大文件的上 ...
- PAT - IO - 螺旋方阵
所谓“螺旋方阵”,是指对任意给定的N,将1到N*N的数字从左上角第1个格子开始,按顺时针螺旋方向顺序填入NxN的方阵里.本题要求构造这样的螺旋方阵. 输入格式: 输入在一行中给出一个正整数N(< ...
- call和apply
在js中经常会看到call和apply,他们实际上就是用于改变this的上下文 经典例子 function pet(words) { this.words=words; this.speak=func ...
- Solr4.8.0源码分析(13)之LuceneCore的索引修复
Solr4.8.0源码分析(13)之LuceneCore的索引修复 题记:今天在公司研究elasticsearch,突然看到一篇博客说elasticsearch具有索引修复功能,顿感好奇,于是点进去看 ...