Chinaunix首页 | 论坛 | 博客
  • 博客访问: 346464
  • 博文数量: 102
  • 博客积分: 3140
  • 博客等级: 中校
  • 技术积分: 680
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-28 11:44
个人简介

开阔未来

文章分类

全部博文(102)

文章存档

2015年(10)

2014年(1)

2013年(1)

2012年(4)

2011年(8)

2010年(24)

2009年(51)

2008年(3)

我的朋友

分类: LINUX

2011-01-18 11:41:34

在分析linux内核的中断,软中断时,先应该明确这样一个派生关系:

irq ==> softirq ==> tasklet ==> bottom half ==> task queue
----------------------------------------------|==> timer

中断是最初的原动力。分时系统依赖于时钟中断来定时重新调度可以运行的程序。外设通过中断来通知cpu处理相关的任务。中断处理程序是内核中一段特殊的,独立的,可运行实体。这个实体,某种程度上,是和进程或线程类似的。

由于中断需要快速处理,因此派生出来软中断softirq来处理中断没有处理完的事情。比如在网卡的驱动程序里,在中断环境里,只是把包放到一个队列里,然后由软中断来把包传递给进程,或者转发包等。

linux的softirq是不可重入的,因此,在单cpu的系统上,一次只能有一个软中断在运行。而在多cpu系统上,可以同时有多个softirq在运行。softirq可以处理更多的任务,一般如协议栈,文件系统等,都可以放到软中断里面处理。

tasklet是在softirq上的一个扩展。它是一段可以执行的代码片断,但是,一个tasklet同时只能在一个cpu上执行(一个tasklet只能有一个活动实体,而一个softirq可以有多个活动实体,但是softirq的活动实体是不可重入的)。

bottom half是linux 2.2.x以前的系统所采用的软中断机制,由于在smp上扩展性不好,现在已经不用了。bh也是一个可执行的代码片断,但是全局只能有一个bh在运行,多个bh之前是线性执行的,所以在smp系统上,浪费比较严重。

task queue是在bh机制上的一个扩展。也是一个可执行的代码片断。其目的是突破bh的数目限制(只有32个)。但是它的执行也是线性的,因此在smp上,也没什么优势。

timer也是bh上的一个扩展。每个timer都是一个可执行的片断,但是它更灵活,所以,如果有优先级高的任务,可以考虑使用timer。

在编写设备驱动时, tasklet 机制是一种比较常见的机制,通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成。
为了最大程度的避免中断处理时间过长而导致中断丢失,有时候我们需要把一些在中断处理 中不是非常紧急的任务放在后面执行,而让中断处理程序尽快返回。在老版本的 linux 中通常将中断处理分为 top half handler 、 bottom half handler 。利用 top half handler 处理中断必须处理的任务,而 bottom half handler 处理不是太紧急的任务。
但是 linux2.6 以后的 linux 采取了另外一种机制,就是软中断来代替 bottom half handler 的处理。而 tasklet 机制正是利用软中断来完成对驱动 bottom half 的处理。 Linux2.6 中软中断通常只有固定的几种: HI_SOFTIRQ( 高优先级的 tasklet ,一种特殊的 tasklet) 、 TIMER_SOFTIRQ (定时器)、 NET_TX_SOFTIRQ (网口发送)、 NET_RX_SOFTIRQ (网 口接收) 、 BLOCK_SOFTIRQ (块设备)、 TASKLET_SOFTIRQ (普通 tasklet )。当然也可以通过直接修改内核自己加入自己的软中断,但是一般来说这是不合理的,软中断的优先级比较高,如果不是在内核处理频繁的任务不建议使用。通常 驱动用户使用 tasklet 足够了。
软中断和 tasklet 的关系如下图:
(如果你看不到此图,也可以从“相册/文中图片”中找到图片。)
tasklet.JPG
   
    上图可以看出, ksoftirqd 是一个后台运行的内核线程,它会周期的遍历软中断的向量列表,如果发现哪个软中断向量被挂起了( pend ),就执行对应的处理函数,对于 tasklet 来说,此处理函数就是 tasklet_action ,这个处理函数在系统启动时初始化软中断的就挂接了。
Tasklet_action 函数,遍历一个全局的 tasklet_vec 链表(此链表对于 SMP 系统是每个 CPU 都有一个),此链表中的元素为 tasklet_struct 。此结构如下 :
struct tasklet_struct
{
       struct tasklet_struct *next;
       unsigned long state;
       atomic_t count;
       void (*func)(unsigned long);
       unsigned long data;
};
每个结构一个函数指针,指向你自己定义的函数。当我们要使用 tasklet ,首先新定义一个 tasklet_struct 结构,并初始化好要执行函数指针,然后将它挂接到 task_vec 链表中,并发一个软中断就可以等着被执行了。
原理大概如此,对于 linux 驱动的作者其实不需要关心这些,关键是我们如何去使用 tasklet 这种机制。
Linux 中提供了如下接口:
DECLARE_TASKLET(name,function,data) :此接口初始化一个 tasklet ;其中 name 是 tasklet 的名字, function 是执行 tasklet 的函数; data 是 unsigned long 类型的 function 参数。
static inline void tasklet_schedule(struct tasklet_struct *t) :此接口将定义后的 tasklet 挂接到 cpu 的 tasklet_vec 链表,具体是哪个 cpu 的 tasklet_vec 链表,是根据当前线程是运行在哪个 cpu 来决定的。此函数不仅会挂接 tasklet ,而且会起一个软 tasklet 的软中断 , 既把 tasklet 对应的中断向量挂起 (pend) 。
两个工作完成后,基本上可以了, tasklet 机制并不复杂,很容易的使程序尽快的响应中断,避免造成中断丢失。
阅读(595) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-03-09 12:15:03

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com