1.linux内核对中断处理函数的要求
明确:CPU资源给进程,软中断,硬件中断使用;
明确:在linux系统中,硬件中断的优先级高于软中断,软中断的优先级高于进程;
明确:优先级是指某个任务获取CPU资源的能力;
明确:在linux系统,硬件中断无优先级;软中断有优先级;进程有优先级;
明确:在linux系统,任务分中断和进程
中断上下文:中断处理的整个流程,过程;
进程上下文:从进程的创建,调度,抢占到最后的消亡的整个过程;
内核对于中断处理函数的要求:
要求中断处理函数的执行速度要快!如果中断处理函数长时间的占用CPU资源,
会导致其他任务(硬件中断,软中断,进程)无法及时获取CPU资源,影响系统的并发和响应能力;
*********内核要求中断处理函数不能进行休眠操作!
*********中断处理函数不隶属于任何进程,所以不会参与进程的调度!
问:内核对于中断处理函数的要求是执行速度要快,但是在某些场合,某些设备可能无法满足内核
的这种要求,对于这种场合和设备的操作势必会影响系统的并发的响应能力;内核如何解决这种问题呢?
答:对于中断处理函数长时间的占有CPU资源的情况,内核建议要求将原先中断处理函数分拆两部分,
分别叫顶半部和底半部;
1.1.内核中断之顶半部
一旦硬件中断触发,内核首先执行顶半部的内容,其实顶半部就是中断处理函数,此时的中断处理函数只做紧急的,耗时较少的内容;目的就是让任务及时释放CPU资源给其它任务使用;
顶半部不能被中断!
1.2.内核中断之底半部
底半部将会做原先中断处理函数中比较耗时,不紧急的内容,此部分内容可以被中断!如果底半部要执行,必须在顶半部要进行登记;是登记而不是调用!
将来CPU在适当的时候去执行底半部的内容:
问:
如何实现底半部呢?
答:
(新版本内核)
实现机制有三种:
tasklet
工作队列
软中断
底半部机制之tasket
明确:tasklet本质上其实就是延后执行的一种手段!********特点:
1.tasklet对应的延后处理函数就是将来就做原先中断处理函数中比较耗时,不紧急的内容;
2.tasklet本身是基于软中断来实现的;
3.tasklet对应的延后处理函数工作在中断上下文中,不能进行休眠操作!
数据结构:
struct tasklet_struct
{
unsigned long data;
void (*func) (unsigned long);
};
作用:描述tasklet
成员:
data:给延后处理函数传递的参数
func:延后处理函数,将来里面做原先中断处理函数中比较耗时,不紧急的内容,在适当
的时候会被执行;形参data保存传递过来的参数
利用tasklet机制后实现的步骤
1.定义初始化名称为name的tasklet对象
DECLARE_TASKLET(name,func,data);
name:tasklet对象名称
func:tasklet延后处理函数
data:给延后处理函数传递的参数,如果不传递给0
例如:
DECLARE_TASKLET(btn_tasklet, btn_func, 0);
2.编写tasklet延后处理函数
static void btn_func (unsigned long data)
{
//data保存传递的参数信息
//将做比较耗时,不紧急的内容
//工作在中断上下文中,不能进行休眠
//硬件中断和高优先级的软中断可以打断
}
3.在顶半部进行登记底半部tasklet延后处理函数,将来在适当的时候调用tasklet的延后处理函数
tasklet_schedule (name);
name:定义初始化的tasklet对象名称
例如:
tasklet_schedule (&btn_tasklet);
底半部机制之工作队列
明确:工作队列的本质就是延后执行!明确:"休眠"只存在于进程的世界里!
特点:
1.工作队列对应的延后处理函数将来做原先中断处理函数中比较耗时,不紧急的内容;
2.工作队列对应的延后处理函数工作在进程上下文中,可以进行休眠操作;
3.工作队列重点研究的是队列中每一个节点,每一个节点代表着延后执行的某个事情;工作队列可以采用内核默认的,当然也可以自己创建一个工作队列;
工作队列每一个节点的数据结构:
struct work_struct {
void (*func)(struct work_struct *work);
...
};
作用:描述工作队列中每一个节点(延后执行的内容)
成员:
func:延后处理函数,工作在进程上下文中,可以进行休眠操作;将来在适当的时候被调用;
形参work指针就是指向自己(节点)
利用工作队列实现延后执行:
1.定义初始化工作队列中某个节点对象
struct work_struct btn_work;
INIT_WORK(&btn_work, btn_work_function);
//给btn_work对象添加一个延后处理函数
2.编写延后处理函数
static void btn_work_function(struct work_struct *work)
{
//做不紧急,耗时较长的内容
//工作在进程上下文
//可以进行休眠操作
//work指针指向btn_work
}
3.在顶半部进行登记延后处理函数,在适当的时候会执行此函数
schedule_work(&btn_work);
案例:在按键驱动程序中利用工作队列实现延后执行
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
//声明描述按键硬件信息的数据结构
struct btn_resource {
int gpio; //GPIO编号
int irq; //中断号
char *name; //按键名称
};
//定义初始化按键的硬件信息
static struct btn_resource btn_info[] = {
[0] = {
.gpio = S5PV210_GPH0(0),
.irq = IRQ_EINT(0),
.name = "KEY_UP"
},
[1] = {
.gpio = S5PV210_GPH0(1),
.irq = IRQ_EINT(1),
.name = "KEY_DOWN"
}
};
//定义工作队列中某个节点(延后执行)
static struct work_struct btn_work;
//底半部延后处理函数
static void btn_work_function(struct work_struct *work)
{
printk("%s\n", __func__);
}
//顶半部(中断处理函数)
static irqreturn_t button_isr(int irq, void *dev)
{
int state;
//通过dev获取按键对应的硬件信息
struct btn_resource *pdata =
(struct btn_resource *)dev;
//登记延后处理函数
schedule_work(&btn_work);
//打印按键的状态值
state = gpio_get_value(pdata->gpio);
printk("%s:按键%s的按键状态为%s\n",
__func__, pdata->name, state ?"松开":"按下");
return IRQ_HANDLED; //中断处理完毕
}
static int btn_init(void)
{
int i;
//申请GPIO资源
//申请中断资源和注册中断处理函数
//注意:
//8个按键共享一个中断处理函数,通过中断号和参数区分
//GPIO为复用IO,先做中断IO,一旦中断触发,内核帮你切换为输入口
for (i = 0; i < ARRAY_SIZE(btn_info); i++) {
gpio_request(btn_info[i].gpio, btn_info[i].name);
request_irq(btn_info[i].irq,
button_isr,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
btn_info[i].name,
&btn_info[i]);
}
//初始化工作队列的某个节点对象
//给节点添加一个延后处理函数
INIT_WORK(&btn_work, btn_work_function);
return 0;
}
static void btn_exit(void)
{
int i;
//释放GPIO资源
//释放中断资源和删除中断处理函数
for (i = 0; i < ARRAY_SIZE(btn_info); i++) {
gpio_free(btn_info[i].gpio);
free_irq(btn_info[i].irq, &btn_info[i]);
}
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");
底半部机制之软中断
特点:
1.也是延后执行的一种手段
2.软中断的延后处理函数工作在中断上下文中,不能进行休眠操作
3.软中断的处理函数可以运行在多CPU中,而基于软中断实现的tasklet的延后处理函数之能运行在一个CPU上;
4.软中断的处理函数要求具有可重入性;
函数可重入:
1.尽量避免访问全局变量
2.如果要访问全局变量,必须要进行互斥访问,造成代码的执行效率变低
5.软中断的实现不能用模块的加载和卸载,必须把代码写到内核源码,静态编译;
如果要更新了代码,后果是需要系统重新启动;
百度:linux内核软中断编程