中断处理机制与工作队列

一、中断处理机制

      中断是一个随机事件,因此如果关中断的时间过长,CPU就不能及时的响应其他的中断请求,从而造成中断的丢失。因此,Linux内核的目标就是尽可能快的处理完中断请求,尽可能的把更多的处理向后推迟。因此,内核把中断分为了两部分:上半部和下半部,上半部(就是中断服务程序),下半部(就是一些内核函数)留着稍后处理。首先,一个快速的”上半部”来处理硬件发出的请求,它必须在一个新的中断产生之前终止。其次,“下半部”运行时是允许中断请求的,而上半部运行时是关中断的。这是二者的主要区别。

1.中断“下半部”

内核到底什么时候执行“下半部”,以何种方式组织“下半部”?

下半部实现机制中,在老的内核中,这个机制叫做bottom-half(BH),但是linux的这种BH机制有两个缺点:

(1)  在任一时刻,系统中只能有一个CPU可以执行BH代码,以防止两个或多个CPU同时来执行BH函数而相互干扰,因此BH代码的执行是严格“串行化的”

(2)  BH函数不允许嵌套

而2.6内核以后,“下半部”机制处理机制有了很大的发展和改进:

(1)  软中断请求机制

(2)  小任务机制

(3)  工作队列机制

2.软中断

      整个softirq机制的设计与实现中自始自终贯彻了一个思想:“谁触发,谁执行”,即触发软中断的那个CPU负责执行它所触发的中断,而且每个CPU都有它自己的软中断触发与控制机制。

2.小任务机制

tasklet机制是一种较为特殊的软中断。

特点:

(1)与一般的软中断不同,某一段tasklet代码在某个时刻只能在一个CPU上运行,而不像一般的软中断服务函数(即softirq_action结构体中的action函数指针)那样------在同一时刻可以被多个CPU并发地执行。

(2)与BH机制不同,不同的tasklet代码在同一时刻可以在多个CPU上并发地执行,而不像BH机制那样必须严格地串行化执行。

linux中用tasklet_struct来描述一个tasklet,每个结构体代表一个独立的小任务

注:tasklet_struct结构体中atomic_t count成员表示对这个tasklet的引用计数值,只有当count为0时,tasklet代码段才能执行,如果count非零,则这个tasklet是被禁止的。任何想要执行一个tasklet代码段的人都首先必须检查其count成员是否为0.

linux在interrupt.h头文件中定义了两个宏来定义tasklet_struct 结构变量的辅助宏:

DECLARE_TASKLET(name,func,data)和DECLARE_TASKLET_DISABLED(name,func,data),显然,DECLARE_TASKLET宏在初始化时是被使能的,因为其count为0,而DECLARE_TASKLET_DISABLED是被禁止的,因为其count为1.

 

小任务机制总结:

(1)  声明和使用小任务大多数情况下,为了控制一个常用的硬件设备,小任务机制是实现下半部的最佳选择。小任务可以动态创建,使用方便,执行起来也比较快,也可以静态创建。选择哪种方式取决于到底是想对小任务进行直接引用还是间接引用,如果准备静态地创建一个小任务(直接引用),使用下面两个宏中的一个:

DECLARE_TASKLET(name,func,data)

DECLARE_TASKLET_DISABLED(name,func,data)

(2)  编写自己的小任务处理程序,小任务处理程序必须符合如下的函数类型:

void tasklet_handler(unsigned long data)

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

(3)  调度自己的小任务通过调用tasklet_schedule()函数

 

3.工作队列

      工作队列是另外一种将工作推后执行的形式,它和tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说这个下半部可以在进程的上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的是工作队列允许被重新调度甚至是睡眠。

那么,什么情况下使用工作队列,什么情况下使用tasklet呢?

如果推后执行的任务需要睡眠,那么就选择工作队列;

如果推后执行的任务不需要睡眠,那么就选择tasklet。

另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也可以使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的IO操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。

我们把推后执行的任务叫做工作,描述工作的结构体是work_struct,这些工作以队列结构组织成工作队列,描述工作队列的结构体为workqueue_struct,而工作线程就是负责执行工作队列中的工作,系统默认的工作线程是events,自己也可以创建自己的工作者线程。

二、工作队列详解

Linux中workqueue机制

为什么需要workqueue?Linux中的workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。

 

Linux内核创建工作队列时:




__alloc_workqueue_key函数会调用kthread_create来进行线程的创建

这个线程以wq为数据执行rescuer_thread这个函数。

 

触摸屏驱动工作队列:

没有使用系统默认的工作队列keventd_wq(这个工作队列是在Linux系统初始化的时候就创建的),因为默认的工作队列存在性能问题。

在初始化workqueue过程中,内核需要初始化内核线程来处理工作队列,注册的内核线程工作比较简单,就是不断的扫描对应的cpu_workqueue_stuct中的任务队列,从中获取一个有效任务,然后执行该任务。

内核线程的创建调用create_singlethread_workqueue(name)来完成,该函数会创建一个工作队列,并且在内部创建了一个内核线程。

 

工作队列的创建方式有两种,如下图所示:


第二种方式中,会创建自己的工作队列,优点是性能高,会创建内核线程来处理工作函数。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值