Linux内核对中断函数的要求

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内核软中断编程

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值