代码学习inux内核驱动(九)
中断、tasklet 工作队列
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/semaphore.h>
#include <linux/wait.h>
#include <linux/kthread.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/workqueue.h>
/******************************************************
中断:是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理。
中断:中断其实可以分为同步中断(软中断)和异步中断(异步中断),软中断又叫异常
硬中断可以被另一个优先级比自己高的硬中断“中断”,不能被同级(同一种硬中断)或低级的硬中断“中断”,更不能被软中断“中断”。
软中断可以被硬中断“中断”,但是不会被另一个软中断“中断”。在一个CPU上,软中断总是串行执行,不需要做同步。
抢占分两种情况:用户抢占和内核抢占
内核不能抢占中断,中断例程中不允许进行进程调度,中断中不能使用睡眠和信号量,中断中不能向用户空间发送或接受数据。
中断对临界资源访问,禁止中断,多处理器还必须使用自旋锁来避免来自其他CPU的干扰,中断不会并行。
共享中断就是将不同的设备挂到同一个中断线上。
内核把中断处理分为两部分:上半部(top-half)和下半部(bottom-half),上半部 (就是中断服务程序)内核立即执行,而下半部(就是一些内核函数)留着稍后处理。
实现中断下半部(可以并发执行,谁触发谁执行):
1) 软中断请求(softirq)机制,用于紧急场合、主要subsystem会用、驱动基本不用
2) 小任务(tasklet)机制 不同片段可并发执行
3) 工作队列机制 允许被重新调度甚至是睡眠
中断函数:
gpio_to_irq
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
IRQF_DISABLED 则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;
IRQF_SHARED 则表示多个设备共享中断,此时、dev_id不能为空
IRQF_SAMPLE_RANDOM 表示对系统熵有贡献,对系统获取随机数有好处
devname: 设置中断名称,cat /proc/interrupts中可以看到此名称。
返回0表示成功
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
thread_fn : 中断线程,类似于中断下半部
IRQF_ONESHOT:表示threadirq线程中关闭该中断,不能与IRQF_SHARED共用
const void *free_irq(unsigned int irq, void *dev_id)
int irq_set_irq_type(unsigned int irq, unsigned int type)
使能或屏蔽中断源:
enable_irq(unsigned int irq);
disable_irq(unsigned int irq); //会等待中断处理完成,再返回
disable_irq_nosync(unsigned int irq);//立即返回
tasklet:
DECLARE_TASKLET(name, func, data)
DECLARE_TASKLET_DISABLED(name, func, data)
void tasklet_schedule(struct tasklet_struct *t)
tasklet_enable tasklet_disable tasklet_kill tasklet_disable_nosync
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
local_bh_disable local_bh_enable :禁止或使能软中断和tasklet 底半部机制
工作队列:
使用缺省工作队列,
struct work_struct wq
INIT_WORK(_work, _func)
INIT_DELAYED_WORK(_work, _func)
schedule_work();
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
void flush_scheduled_work(void); 刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行
int cancel_delayed_work(struct work_struct *work); 取消延迟工作
cancel_work_sync
新建工作队列:
struct workqueue_struct
struct workqueue_struct *create_workqueue(const char *name);
int queue_work(struct workqueue_struct *wq, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay);
void flush_workqueue(struct workqueue_struct *wq);
void destroy_workqueue(struct workqueue_struct *wq);
软中断:
struct softirq_action
void open_softirq(int nr, void (*action) (struct softirq_action *))
void raise_softirq(unsigned int nr)
内核定义了10种软中断,一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,
NR_SOFTIRQS
软中断的守护进程ksoftirqd
但中断默认没有导出符号,驱动中并不能使用、需要在softirq.c中去修改代码导出符号。
cat /proc/kallsyms 查看内核符号
modinfo linux-bcm-core.ko 查看依赖
******************************************************/
MODULE_AUTHOR("xyzeng");
MODULE_LICENSE("Dual BSD/GPL");
#define IRQ_GPIO (4*32 + 1)
int irq;
struct work_struct wk;
int stop;
struct workqueue_struct * wq;
void irq_workqueue(struct work_struct *work)
{
stop =1;
while(stop){
msleep(5000);
printk("irq_workqueue enter!\n");
}
printk("[%s,%d] out!\n",__FUNCTION__,__LINE__);
}
void irq_tasklet(unsigned long data)
{
local_bh_disable();
printk("irq_tasklet enter!\n");
local_bh_enable();
}
DECLARE_TASKLET(hello_tasklet, irq_tasklet, 1);
irqreturn_t irq_srv(int irq, void * dev_id)
{
printk("irq_srv enter!\n");
schedule_work(&wk);
tasklet_schedule(&hello_tasklet);
raise_softirq(TASKLET_SOFTIRQ);
return IRQ_HANDLED;
}
void do_my_softirq(struct softirq_action * action )
{
printk("do_my_softirq enter!\n");
}
static int code_case_irq_init(void)
{
printk("[%s,%d] enter!\n",__FUNCTION__,__LINE__);
wq = create_workqueue("zeng");
INIT_WORK(&wk, irq_workqueue);
queue_work(wq, &wk);
int ret = gpio_request(IRQ_GPIO,"gpio_irq");
if(ret == 0){
irq = gpio_to_irq(IRQ_GPIO);
irq_set_irq_type(irq,IRQ_TYPE_EDGE_RISING);
ret = request_irq(irq,irq_srv,IRQF_SHARED,"hello",(void *)&irq);
printk("gpio_to_irq:%d,request_irq:%d\n",irq,ret);
}
open_softirq(TASKLET_SOFTIRQ, do_my_softirq);
//local_bh_disable();
// printk("[%s,%d] enter 2!\n",__FUNCTION__,__LINE__);
//local_bh_enable();
return 0;
}
static void code_case_irq_exit(void)
{
printk("[%s,%d] enter!\n",__FUNCTION__,__LINE__);
if(irq > 0)
free_irq(irq,(void *)&irq);
gpio_free(IRQ_GPIO);
stop = 0;
flush_workqueue(wq);
destroy_workqueue(wq);
printk("[%s,%d] out!\n",__FUNCTION__,__LINE__);
}
module_init(code_case_irq_init);
module_exit(code_case_irq_exit);