linux内核模块编程(七)----中断下半部之tasklet

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
在这里插入图片描述

一 why

前面的博文中,我们已经讨论过了中断,以及中断的实现方式。那么何为中断下半部呢?既然有下半部,那肯定有上半部,那么上半部又是什么意思?
我们知道,当发生中断时,CPU会被抢断,当前正在执行的线程会被打断,CPU会跳转到中断服务程序中执行,如下图所示
在这里插入图片描述
那么为什么需要中断下半部呢?
我们知道,中断的一个特点是,会抢断CPU,造成其他进程无法执行,如果一个中断处理程序执行时间很长,会带来问题。比如一个线程申请了一把锁,此时来了一个中断,线程被打断,线程不会及时释放这把锁,但是由于中断时间很长,该线程长时间占着这把锁,没有及时释放,而其他线程又需要申请这把锁,导致其他线程长时间申请不到锁的资源,该线程的执行就会出现问题。
那么linux就引入了下半部,去解决这个问题,将中断中处理耗时比较长的部分延后,放到了下半部去处理。

二 what

中断安下半部的实现机制有三种,分别是

1. softirq: 处理比较快,它是内核级别的机制,如果需要这个机制需要修改整个内核源码
	不推荐也不常用
2. tasklet: 内部实现实际上调用了softirq
3. workqueue: 工作队列

今天,我们先看看tasklet的实现方式,如何在驱动中通过tasklet实现下半部处理

三 how

对驱动程序来说,我们要做的主要就是构建一个tasklet的结构体,并且把它注册到内核中,那么它何时才能被调度,完全是由内核自己的调度节奏决定,我们的驱动并不能决定tasklet中的方法何时被执行
在这里插入图片描述
a. 初始化
根据结构体可知,我们最主要要实现的就是tasklet的实现方法函数定义

// 主要的一个结构体
struct tasklet_struct
	{
		struct tasklet_struct *next;
		unsigned long state;
		atomic_t count;
		void (*func)(unsigned long);    //下半部的实现方法
		unsigned long data;		// 传递给方法 func
	};
//初始化
tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data)

b. 启动

//启动下半部tasklet,注意这个启动函数必须是在中断上半部中被调用
tasklet_schedule(struct tasklet_struct * t)

c. 销毁

//一般地有初始化,就会有销毁,一般是在模块卸载exit中调用
tasklet_kill(struct tasklet_struct * t)

一般步骤

1. 驱动中先注册一个中断
2. 注册中断的时候,同时初始化中断下半部
3. 实现中断处理函数,在中断处理程序中,启动下半部
4. 实现下半部处理函数

一般框架如下

static int __init key_drv_init(void)
{
	int ret = 0;

	//1. 设定一个全局的设备对象
	key_dev = (key_desc_t *)kzalloc(sizeof(key_desc_t), GFP_KERNEL);
	if (key_dev == NULL) {
		printk("kzalloc failure\n");
		return -EINVAL;
	}
	
	......

	//4. 注册中断
	key_dev->irqno = get_irqno_from_node();
	ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
		"key3_exit10", NULL);

	// 初始化中断下半部tasklet
	tasklet_init(&key_dev->mytasklet, key_irq_bottom_half_tasklet, 1);
	return 0;
}

中断处理函数

irqreturn_t key_irq_handler(int irq_no, void *devid)
{
	printk("-----------------\n");

	//启动中断下半部开始调度
	tasklet_schedule(key_dev->mytasklet);
	......

	return IRQ_HANDLED;
}

中断下半部处理函数

void key_irq_bottom_half_tasklet(unsigned long data)
{
	......
}

注销中断下半部

static void __exit key_drv_exit(void)
{
	printk(KERN_INFO "new exit chardev0\n");
	tasklet_kill(&key_dev->mytasklet);
	......
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值