Linux内核学习之中断 中断本质【转】
转自:http://www.linuxidc.com/Linux/2011-11/47657.htm
[中断概述]
中断本质上是一种特殊的电信号,由硬件设备发向处理器。异常和中断的不同是异常在产生时必须考虑与处理器时钟同步。实际上异常也常常称为同步中断。比如在除0或者缺页时,必须靠内核处理的时候,处理器就会产生一个异常。
[中断处理机制的实现]
中断从硬件到内核的路由
设备产生中断,通过总线把电信号发送给中断控制器。如果中断线是激活的(它们允许被屏蔽的),那么中断控制器就会把中断发往处理器。在大多数体系结构中,这个工作就是通过电信号给处理器的特定管脚发送一个信号。除非在处理器上禁止该中断,否则处理器会立即停止它正在做的事,关闭中断系统,然后跳到内存中预定的位置开始执行那里的代码。这个预定义的位置是由内核设置的,是中断处理程序的入口点。
[中断处理程序]
在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序(interrupt handler)也叫做中断服务例程(interrupt service routine)。
中断处理程序就是一个普普通通的C函数,但是它与其他内核函数的真正区别在于,中断处理程序时被内核调用来响应中断的,它运行于中断上下文中。
注册中断处理程序:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_thandler, unsigned long flags,
const char *name, void *dev)
irq:分配的中断号
handler:中断处理函数程序的指针 typedefirqreturn_t (*irq_handler_t)(int, void *);
flags:中断类型
name:与中断相关的设备的名字;
dev:用于共享中断,因为可能在一条中断线上有几个设备,dev用来区分是哪个设备产生的中断
中断上下文和进程上下文对比
|
中断上下文 |
进程上下文 |
定义 |
当执行一个中断处理程序时,内核处于中断上下文中 |
当程序调用了系统调用或者触发了某个异常,它就陷入了内核空间,此时,内核代表进程执行并处于进程上下文中。 |
睡眠情况 |
不可睡眠,不能被调度,也就是说中断上下文中不能使用有可能睡眠的函数 |
可睡眠,可调度 |
同步机制 |
自旋锁 |
都可 |
|
||
|
[中断上半部和下半部]
首先问自己一个问题,为什么要把中断分为上半部和下半部,难道一个就放在中断处理程序中不好吗?答案是否定的
1、中断可以随时的打断其他正在执行的程序,如果被打断的代码对系统很重要,那么此时中断处理程序的执行时间应该是越短越好;
2、中断处理程序正在执行时,会屏蔽同条中断线上的中断请求;而更严重的是,如果设置了IRQF_DISABLED,那么该中断服务程序执行时会屏蔽所有其他的中断请求。那么此时应该让中断处理程序执行的越快越好。
上半部:一个快速、异步而简单的处理程序专门来负责对硬件的中断请求做出快速响应,与此同时也要完成那么些对时间要求很严格的操作;
下半部:那么对时间要求相对宽松,其他的剩余工作会在稍后的任意时间执行。
下面是对上半部和下半部的工作划分:
如果一个任务对时间非常敏感,将其放在中断处理程序中执行;
如果一个任务中和硬件相关,将其放在中断处理程序中执行;
如果一个任务要保证不被其他中断(特别是相同的中断)打断,将其放在中断处理程序执行;
其他所有任务,都应考虑放在下半部执行。
[下半部机制]
软中断
软中断保留给系统中对时间要求最严格以及最重要的下半部使用,目前只有两个子系统(网络和scsi)直接使用软中断。
Tasklet
Tasklet是利用软中断实现的一种下半部机制。在选择使用软中断还是tasklet时,建议使用tasklet,除了网络和SCSI情况。相比软中断,tasklet的接口更简单,锁保护要求较低。
工作队列
工作队列是可以把工作推后交由一个内核线程去执行,下半部总是会在进程上下文中执行,允许重新调度和睡眠。
三种下半部机制对比
下半部 |
上下文 |
顺序执行保障 |
软中断 |
中断 |
没有 |
Tasklet |
中断 |
同类型不能同时执行 |
工作队列 |
进程 |
没有(和进程上下文一样被调度) |
[概述]
工作队列是一种将工作推后执行的的形式,工作队列可以把工作推后,交由一个内核线程去执行,占有进程上下文的所有优势,允许重新调度和睡眠。
[工作队列的实现]
工作队列子系统是一个用于创建内核线程的接口,通过它创建的进程负责执行由内核其他部分排到队列里的任务。它创建的这些内核线程称作工作者线程(worker thread)。工作队列子系统提供了一个缺省的工作者线程来处理推后的工作,所以我们大多数情况下,没必要自己去创建工作者线程,使用默认的就OK。
Linux kernel development 3 上对工作队列的实现原理讲的很清楚了,这里就不再详述。具体来看一个实例
[实例]
- static struct input_dev *button_dev;
- static struct work_struct button_wq;
在做实际的驱动开发的时候,不推荐使用全局变量,因为全局变量是造成竞争条件的主要原因。
[中断处理程序]
- static irqreturn_t button_interrupt(intirq, void *p)
- {
- schedule_work(&button_wq);
- returnIRQ_RETVAL(IRQ_HANDLED);
- }
schedule_work(&work)函数用来调度work,把work提交给缺省的events工作者线程(插入工作任务链表中)。work马上会被调度,一旦其所在的处理器上的工作者线程被唤醒,唤醒的工作者队列会遍历整个工作任务链表,如果有工作,它就会被执行,否则继续睡眠。
Schedule_work_delay(&work, delay) 和schedule_work函数的作用相同,唯一区别就是延迟delay时间后才会执行。
[工作队列处理函数]
- void work_handler(void *data)
- {
- /*get pin value <down 0, up 1> */
- intval = s3c2410_gpio_getpin(S3C2410_GPG(0));
- input_report_key(button_dev,KEY_1, val);
- input_sync(button_dev);
- }
完成按键信息的获取和上报工作。
[模块初始化函数]
- static int __init button_init(void)
- {
- interr;
- if(request_irq(BUTTON_IRQ, button_interrupt,
- IRQ_TYPE_EDGE_BOTH,DEV_NAME, NULL)) {
- printk(KERN_ERR"cannotallocate irq");
- return- EBUSY;
- }
- ……
- INIT_WORK(&button_wq,work_handler);
- printk("initialized\n");
- return0;
- }
初始化函数主要完成了两个工作:
1、 申请中断
2、 创建一个推后的工作button_wq,有两种方法:
动态创建:
INIT_WORK(struct work_struct work, void (*func)(void *)), 在新内核里,INIT_WORK已经发生了变化,少了第三个参数。可以参考http://www.linuxidc.com/Linux/2011-11/47658.htm
静态创建:
DECLARE_WORK(name,func) 创建一个名为name,处理函数为func的work_struct结构体。
不管是动态还是静态,都是填充work struct 结构体。
有兴趣可以去看内核里gpio_keys.c的实现代码,一个典型的work queue的实现例子
[概述]
Tasklet是软中断的特殊实现。Tasklet通常是下半部处理的优选机制,它在性能和易用性之间有着很好的平衡。较softirq,tasklet不需要考虑SMP下的并发问题,而又比workqueue有着更好的性能。
不同类型的tasklet可以在不同的处理器上同时执行,但同类型的tasklet不能同时执行。这就避免了并发问题。
ksoftirqd内核线程有着类似工作者线程一样的职责,它会周期的遍历软中断的向量列表,如果发现哪个软中断向量挂起了(pending),就执行相应的处理函数,
关于tasklet的实现同样可以拜读LKD3,这书真的很经典。
[实例]
- static struct input_dev *button_dev;
- static struct tasklet_structbutton_tasklet;
在做实际的驱动开发的时候,不推荐使用全局变量,因为全局变量是造成竞争条件的主要原因。
[中断处理程序]
- static irqreturn_t button_interrupt(intirq, void *p)
- {
- tasklet_schedule(&button_tasklet);
- returnIRQ_RETVAL(IRQ_HANDLED);
- }
调用tasklet_schedule函数,把button_tasklet加入到tasklet_vec(软中断向量)链表中,并触发软中断,这样在下一次调用do_softirq函数时就会执行button_tasklet。Button_tasklet会在尽短的时间内执行。
[tasklet处理函数]
- void tasklet_handler(unsigned long data)
- {
- /*get pin value <down 0, up 1> */
- intval = s3c2410_gpio_getpin(S3C2410_GPG(0));
- input_report_key(button_dev,KEY_1, val);
- input_sync(button_dev);
- }
因为是靠软中断实现的,所以tasklet不能睡眠,这意味着不能在tasklet中使用信号量和其他可能发生阻塞的函数。但是tasklet运行时允许响应中断。
- static int __init button_init(void)
- {
- interr;
- if(request_irq(BUTTON_IRQ, button_interrupt,
- IRQ_TYPE_EDGE_BOTH,DEV_NAME, (void *)button_dev)) {
- printk(KERN_ERR"cannotallocate irq");
- return- EBUSY;
- }
- ……
- tasklet_init(&button_tasklet,tasklet_handler, 0);
- debug("initialized\n");
- return0;
- }
动态初始化tasklet:
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
t:tasklet_struct结构体指针
func:tasklet处理函数指针
data:func的参数
静态初始化tasklet:
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0,ATOMIC_INIT(0), func, data }
Linux内核学习之中断 中断本质【转】的更多相关文章
- (笔记)Linux内核学习(五)之中断推后处理机制
一 中断 硬件通过中断与操作系统进行通信,通过对硬件驱动程序处注册中断处理程序,快速响应硬件的中断. 硬件中断优先级很高,打断当前正在执行的程序.有两种情况: 硬件中断在中断处理程序中处理 硬件中断延 ...
- 向linux内核中添加外部中断驱动模块
本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...
- 关于Linux内核学习的误区以及相关书籍介绍
http://www.hzlitai.com.cn/article/ARM9-article/system/1605.html 写给Linux内核新手-关于Linux内核学习的误区 先说句正经的:其实 ...
- Linux 内核学习经验总结
Linux 内核学习经验总结 学习内核,每个人都有自己的学习方法,仁者见仁智者见智.以下是我在学习过程中总结出来的东西,对自身来说,我认为比较有效率,拿出来跟大家交流一下. 内核学习,一偏之见:疏漏难 ...
- Linux内核学习笔记-2.进程管理
原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...
- Linux内核学习笔记-1.简介和入门
原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...
- Linux内核分析——Linux内核学习总结
马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核学习总结 一 ...
- Linux内核学习笔记二——进程
Linux内核学习笔记二——进程 一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...
- Linux内核学习总结(final)
Linux内核学习总结 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ...
- linux内核学习-
我的博客:www.while0.com 1.端口地址的设置主要有统一编址和独立编址. cat /proc/ioports 可以查询linux主机的设备端口. 2.数据传输控制方式有循环查询,中断和D ...
随机推荐
- 企业级Tomcat部署配置
1.1 Tomcat简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人 ...
- linux 进程间通信之pipe
在实际开发过程中,程序员必须让拥有依赖关系的进程集协调,这样才能达到进程的共同目标. 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内 ...
- hash function比较
http://blog.csdn.net/kingstar158/article/details/8028635 由于工作需要,针对千万级别的数据,使用stl::map着实存在着效率问题,最后使用bo ...
- 【转】给大家分享一下目前mlc颗粒的内存卡资料
以下信息是LZ从其它论坛上找到的TF卡也是有讲究的,一分价钱一分货 dboy99 楼主 骚(6) #1楼 2015-8-5 14:49引用Micro SD卡也叫TF卡,作为手机扩展存储空间的唯一方式用 ...
- 基于C#的PISDK研究(理论)
本篇文章主要对PISDK体系结构以及重点类进行阐述. 当我们决定使用PISDK时,可能会使用到下面的类库: 在上表中,PISDK.dll为核心类,大部分主要功能都在该类中.PISDKCommon.dl ...
- 【EasyNetQ】- 快速开始
欢迎来到EasyNetQ.本指南向您展示如何在大约10分钟内启动并运行EasyNetQ. 你可以在GitHub上找到这个快速入门的代码:https://github.com/mikehadlow/Ea ...
- [bzoj4071] [Apio2015]巴邻旁之桥
Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 10000 ...
- [NOIP2017 TG D2T2]宝藏(模拟退火)
题目大意:$NOIPD2T2$宝藏 题解:正常做法:状压DP .这次模拟退火,随机一个排列,$O(n^2)$贪心按排列的顺序加入生成树 卡点:没开$long\;long$,接受较劣解时判断打错,没判$ ...
- Windows关机过程分析与快速关机
原文链接:http://blog.csdn.net/flyoxs/article/details/3710367 Windows开机和关机慢,很多时候慢得令人抓狂.特别是做嵌入式开发时(如XPE和Wi ...
- BZOJ2729 [HNOI2012]排队 【高精 + 组合数学】
题目链接 BZOJ2729 题解 高考数学题... 我们先把老师看做男生,女生插空站 如果两个老师相邻,我们把他们看做一个男生,女生插空站 对于\(n\)个男生\(m\)个女生的方案数: \[n!m! ...