中断处理(注册、底半部)

本次主要罗列一些API函数和对其进行一些解释,具体使用方法需要参考驱动文件。

中断处理
1.注册中断处理函数
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
                   const char *name, void *dev)

中断处理函数原型:
irqreturn_t handler(int , void *);



2.查看系统中的中断号
/proc/interrupts

3.中断控制
local_irq_disable() 禁止本地中断传递
local_irq_enable() 激活本地中断传递

unsigned long flags;
local_irq_save(flags) 保存本地中断传递的当前状态,然后禁止本地中断传递
local_irq_restore(flags) 恢复本地中断状态到给定的状态

void disable_irq(unsigned int irq) 禁止给定中断线,并确保该函数返回之前在该中断线上没有处理程序在运行
void disable_irq_nosync(unsigned int irq) 禁止给定中断线
void enable_irq(unsigned int irq) 激活给定中断线
void synchronize_irq(unsigned int irq) 等待一个特定的中断处理程序的退出

注意:disable_irq调用了两次enable_irq也需要调用两次才能激活;
irqs_disable() 如果本地中断传递被禁止,则返回非0;否则返回0
in_interrupt() 如果在中断上下文中,则返回非0,如果在进程上下文中,则返回0
in_irq() 如果当前正在执行中断处理程序,则返回非0,否则,返回0

4.释放中断
void free_irq(unsigned int irq, void *dev)

中断下半部的处理方法

一.软中断
1.分配索引
在<Linux/interrupt.h>中定义一个枚举类型来静态的声明软中断;
索引号小的软中断在索引号大的软中断之前执行;
新项可能插在BLOCK_SOFTIRQ和TASKLET_SOFTIRQ之间。

2.注册处理程序
在运行时调用open_softirq()注册软中断处理程序。
例如:
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); //对TASKLET_SOFTIRQ软中断进行初始化
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); //对HI_SOFTIRQ软中断进行初始化
注意该函数在不同版本内核中参数可能不同

3.触发软中断
例如:
raise_softirq(NET_TX_SOFTIRQ);
先禁止中断,触发软中断,最后回复原来的中断状态;

raise_softirq_irqoff(NET_TX_SOFTIRQ);
这个形式是确保调用该函数时已经禁止了中断。

二.tasklet
1.声明tasklet
静态声明:
DECLARE_TASKLET(name,func, data)
DECLARE_TASKLET_DISABLED(name,func, data)
这两个宏都能根据给定的名字静态地创建一个tasklet_struct结构。
当该小任务被调度以后,给定的函数func会被执行,它的参数由data给出。
这两个宏之间的区别在于引用计数器的初始值设置不同。
第一个宏把创建的小任务的引用计数器设置为0,因此,该小任务处于激活状态。
另一个把引用计数器设置为1,所以该小任务处于禁止状态,必须调用tasklet_enable才可以处于激活状态。

动态声明:
struct tasklet_struct my_tasklet; //该变量可以动态分配
tasklet_init(&my_tasklet,tasklet_handler, data);

2.编写tasklet处理程序
编写自己的小任务处理程序小任务处理程序必须符合如下的函数类型:
void tasklet_handler(unsigned long data)

注意:由于tasklet不能睡眠,因此不能在tasklet中使用信号量或者其它产生阻塞的函数。
但是tasklet运行时可以响应中断。

3.调度tasklet
tasklet_schedule(&my_tasklet); /*把my_tasklet标记为挂起 */
调度自己的tasklet通过调用tasklet_schedule()函数并传递给它相应的tasklt_struct指针,该tasklet就会被调度以便适当的时候执行。

可以调用tasklet_disable()函数来禁止某个指定的tasklet。
如果该tasklet当前正在执行,这个函数会等到它执行完毕再返回。

tasklet_enable(&my_tasklet); /* tasklet现在被激活*/
也可以调用tasklet_kill()函数从挂起的队列中去掉一个tasklet。

三.工作队列
1.初始化指定工作,目的是把用户指定的函数_func及_func需要的参数_data赋给work_struct的func及data变量。
DECLARE_WORK(work, func)

struct work_struct work;
INIT_WORK(&work, func)

//
DECLARE_DELAYED_WORK(work, func)
INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func);

func的函数原型:
typedef void (*work_func_t)(struct work_struct *work);

2.对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。
工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型为“events”的工作者线程,
当调用schedule_work时,这个工作者线程会被唤醒去执行工作链表上的所有工作。
static inline bool schedule_work(struct work_struct *work)

3.延迟执行工作,与schedule_work类似。
static inline bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)

4.刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行。
void flush_scheduled_work(void);

5.flush_scheduled_work并不取消任何延迟执行的工作,因此,如果要取消延迟工作,应该调用cancel_delayed_work。
bool cancel_delayed_work(struct delayed_work *dwork);

以上均是采用缺省工作者线程来实现工作队列,其优点是简单易用,缺点是如果缺省工作队列负载太重,
执行效率会很低,这就需要我们创建自己的工作者线程和工作队列。

1.创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。
struct workqueue_struct *create_workqueue(const char *name);

2.类似于schedule_work,区别在于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。
static inline bool queue_work(struct workqueue_struct *wq, struct work_struct *work)

3.延迟执行工作。
static inline bool queue_delayed_work(struct workqueue_struct *wq,
struct delayed_work *dwork, unsigned long delay)

4.刷新指定工作队列。
void flush_workqueue(struct workqueue_struct *wq);

5.释放创建的工作队列。
void destroy_workqueue(struct workqueue_struct *wq);


以上API均为新接口(2.6之后),下面列出旧版API

INIT_WORK(_work, _func, _data);
int schedule_work(struct work_struct *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);

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);

posted on 2016-10-19 19:24  黑大米  阅读(1099)  评论(0编辑  收藏  举报

导航