中断的顶半部和底半部介绍以及实现方式(tasklet 和 工作队列)

1、中断处理程序的注意点

(1)中断上下文中,不能和用户空间交互数据,因为可能导致休眠或者阻塞;
(2)在程序执行完之前不能交出CPU,不能休眠,不能schedule;
(3)中断处理程序要尽可能短,越长则导致响应性能越差;
(4)中断处理程序和一般的处理程序不一样,中断处理程序不参与调度,所以一旦交出CPU就不会再被调度;

2、中断为什么分顶半部和底半部

(1)中断处理程序需要完成一定数量的工作,其中中断处理程序可能很快能完成要处理的工作,也可能要完成的工作比较耗时;
(2)中断处理程序在执行期间会禁止中断,所以中断处理程序要尽快结束不能使中断阻塞时间过长,否则可能会造成中断丢失;
(3)当中断处理程序要处理的任务客观上就比较耗时的时候,就存在工作和速度上的矛盾,一方面任务本身就比较耗时,一方面耗时就会导致中断响应变慢;
(4)针对第三种情况,于是就把中断分为上下半部,将不耗时但必须的操作放在上半部,把耗时的操作放在中断下半部,然后上半部调用下半部;

3、中断顶半部和底半部处理原则

(1)将必须立即处理的极少量任务放入中断的顶半部,此时需要屏蔽于自己同类型的中断,由于任务量少,所以可以迅速不受打扰的处理完紧急任务;
(2)将不太紧急的需要消耗大量事件的任务放到中断的底半部;

4、中断顶半部和底半部两种实现方式

4.1、两种方式的选用原则

(1)需要较少时间的中等数量的急迫任务放在tasklet中。此时不会屏蔽任何中断(包括与自己的顶半部同类型的中断),所以不影响顶半部对紧急事务的处理;同时又不会进行用户进程调度,从而保证了自己急迫任务得以迅速完成。
(2)需要较多时间且并不急迫(允许被操作系统剥夺运行权)的大量任务放在workqueue中。此时操作系统会尽量快速处理完这个任务,但如果任务量太大,期间操作系统也会有机会调度别的用户进程运行,从而保证不会因为这个任务需要运行时间将其它用户进程无法进行。
(3)可能引起睡眠的任务放在workqueue中。因为在workqueue中睡眠是安全的。在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,用workqueue很合适。
总结:tasklet处理耗时相对较少的底半部,操作是原子的,必须一次运行完,不能让出CPU,不能休眠,但是处理速度快;工作队列处理耗时较长的底半部,参与系统调动,可以让出CPU也可以休眠;

4.2、tasklet示例代码

//中断处理底半部
void func(unsigned long data)
{
	int flag;
	
	printk("key-s5pv210: this is bottom half\n");
	
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0));		// input妯″紡
	flag = gpio_get_value(S5PV210_GPH0(2));
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));		// eint2妯″紡

	input_report_key(button_dev, KEY_LEFT, !flag);
	input_sync(button_dev);
}

//定义tasklet并绑定中断下半部
DECLARE_TASKLET(mytasklet, func, 0);

//中断处理程序的顶半部
static irqreturn_t button_interrupt(int irq, void *dummy) 
{ 
	//做一些中断必要的处理
	printk("key-s5pv210: this is top half\n");
	
	//调用中断的底半部
	tasklet_schedule(&mytasklet);
	
	return IRQ_HANDLED; 
}

//绑定中断处理程序
request_irq(BUTTON_IRQ, button_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button-x210", NULL);

4.3、工作队列示例代码

//中断处理底半部
void func(unsigned long data)
{
	int flag;
	
	printk("key-s5pv210: this is bottom half\n");
	
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0));		// input妯″紡
	flag = gpio_get_value(S5PV210_GPH0(2));
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));		// eint2妯″紡

	input_report_key(button_dev, KEY_LEFT, !flag);
	input_sync(button_dev);
}

//定义工作队列并绑定中断底半部
DECLARE_WORK(mywork, func);

//中断处理程序的顶半部
static irqreturn_t button_interrupt(int irq, void *dummy) 
{ 
	//做一些中断必要的处理
	printk("key-s5pv210: this is top half\n");
	
	//调用中断的底半部
	schedule_work(&mywork);
	
	return IRQ_HANDLED; 
}

//绑定中断处理程序
request_irq(BUTTON_IRQ, button_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button-x210", NULL);
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在起飞的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值