Linux 中断下半部
为什么使用中断下半部?
中断执行的原则是要以最快的速度执行完,而且期间不能延时和休眠!
可是现实中,中断中可能没办法很快的处理完需要做的事,或者必须用到延时和休眠,因此引入了中断下半部。
中断中处理紧急事务,其余的交给中断下半部处理。
怎样将代码合理有效的分配给中断和中断下半部?(以后发现新的再添加)
与硬件有关的给中断处理程序(比如按键中断后判断电平),其余的给中断下半部;
紧急事务给中断处理程序,相对不紧急的给中断下半部;
不能被中断的给中断处理程序,剩余的给中断下半部;
没有延时或休眠的给中断处理程序,有休眠或延时的给中断下半部(延时只能用工作队列);
中断下半部有三种实现方法:软中断,小任务,工作队列
1、软中断---softirq
软中断一般用在内核级别的代码中,现在不讨论
2、小任务---tasklet
tasklet中不能延时或休眠
struct tasklet_struct { struct tasklet_struct *next; unsigned long state; // tasklet 状态 atomic_t count; // 锁计数器 void (*func)(unsigned long); // tasklet 处理函数 unsigned long data; // 传递给 tasklet 处理函数的参数 };
// 功能:初始化 tasklet
// 参数1:被初始化的 tasklet
// 参数2:中断下半部函数入口
// 参数3:传递给中断下半部函数的参数
static inline void tasklet_init(struct tasklet_struct *tasklet, void (*func)(unsigned long), unsigned long data);
// 功能:启动下半部
// 参数:tasklet
static inline void tasklet_schedule(struct tasklet_struct *tasklet);
// 功能:释放 tasklet
// 参数: tasklet
void tasklet_kill(struct tasklet_struct *t);
struct key_event { int name; int value; }; struct samsung { int major; struct class *cls; struct device *dev; struct key_event key; struct tasklet_struct tasklet; }; static struct samsung *key_dev; void tasklet_key_irq(unsigned long data) { printk("___^_^___ %s\n", __FUNCTION__); } // 中断处理程序,多个按键可以根据 irqno 区分 irqreturn_t key_irq_handler(int irqno, void *dev_id) { int ret; printk("------------%s-------------\n", __FUNCTION__); ret = gpio_get_value(key_info[i].key_gpio); ret ? (key_dev->key.value = 0) : (key_dev->key.value = 1); printk("key = %d status = %d\n", key_dev->key.name, key_dev->key.value); tasklet_schedule(&key_dev->tasklet); // 返回值一定要是 IRQ_HANDLED return IRQ_HANDLED; } static int __init key_drv_init(void) { ... ... key_dev->irqno = IRQ_EINT(2); ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key_eint", NULL); tasklet_init(&key_dev->tasklet, tasklet_key_irq, 110); ... ... } static int __exit key_drv_exit(void) { ... ... tasklet_kill(&key_dev->tasklet); free_irq(key_dev->irqno,NULL); ... ... }
3、工作队列---work queue
work queue 中不能延时或休眠
// 功能:初始化work
// 参数1:被初始化的work
// 参数2:中断下半部的入口函数
INIT_WORK(_work, _func)
// 功能:启动下半部
// 参数:work
int schedule_work(struct work_struct *work);
// 功能:释放work
// 参数:work
bool cancel_work_sync(struct work_struct *work);
struct key_event { int name; int value; }; struct samsung { int major; struct class *cls; struct device *dev; struct key_event key; struct work_struct work; }; static struct samsung *key_dev; void work_key_irq(struct work_struct *work) { printk("___^_^___ %s\n", __FUNCTION__); } // 中断处理程序,多个按键可以根据 irqno 区分 irqreturn_t key_irq_handler(int irqno, void *dev_id) { int ret; printk("------------%s-------------\n", __FUNCTION__); ret = gpio_get_value(key_info[i].key_gpio); ret ? (key_dev->key.value = 0) : (key_dev->key.value = 1); printk("key = %d status = %d\n", key_dev->key.name, key_dev->key.value); schedule_work(&key_dev->work); // 返回值一定要是 IRQ_HANDLED return IRQ_HANDLED; } static int __init key_drv_init(void) { ... ... key_dev->irqno = IRQ_EINT(2); ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key_eint", NULL); INIT_WORK(&key_dev->work, work_key_irq); ... ... } static int __exit key_drv_exit(void) { ... ... cancel_work_sync(&key_dev->work); free_irq(key_dev->irqno,NULL); ... ... }