Chinaunix首页 | 论坛 | 博客
  • 博客访问: 217545
  • 博文数量: 49
  • 博客积分: 2101
  • 博客等级: 大尉
  • 技术积分: 525
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-07 10:38
文章分类

全部博文(49)

文章存档

2010年(49)

我的朋友

分类: 嵌入式

2010-09-07 15:59:34

1 linux下半部机制

中断处理程序分为上半部和下半部。上半部的接口是唯一的,但是下半部实现的方法有很多。下半部(bottom half)实现的方法称为下半部机制。下述表格摘自《linux内核设计与实现》,可以清楚的说明下半部机制的状态。

下半部机制

状态

BH

2.5中去除

任务队列(task queue

2.5中去除

软中断(softriq

2.3中引入

Tasklet

2.3中引入

工作队列(work queue

2.5中引入

对我们来说,最重要的是软中断和tasklet

 

2 软中断

2.1 软中断定义

       这里的软中断和系统调用里面提到的“软件中断”是截然不同的。“软件中断”实际是仍然是CPU的硬件中断,只是该中断由软件来“触发”而已;但是软中断仅仅是软件上的一种机制。

       软中断是在编译期间静态分配的。在kernel/softirq.c中定义了如下软中断:

static struct softirq_action softirq_vec[NR_SOFTIRQS]

软中断最多允许32个,但是在2.6.31内核中,NR_SOFTIRQS值为9;也就是说系统默认提供了9个软中断。

类比于硬件中断,每个软中断都有自己的处理函数。softirq_action结构(2.6.31)定义如下:

struct softirq_action

{

       void (*action)(struct softirq_action *);

};

其中softirq_action函数指针就指向该软中断的“中断处理函数”。

当上半部执行结束,应该使能对应的软中断;这样当软中断被调度时(do_softirq()),会对所有的软中断遍历,并执行使能的软件断处理函数。

2.2 软中断的使用

       按照上半部和下半部理论,每个硬件中断都应该对应一个软中断,这样可以保证每一个上半部(硬件中断处理函数)都能对应一个下半部(软件中断处理函数)。但是问题随之而来:CPU的硬件中断数量有可能超过32。于是,软中断的是使用分成了两个类别。

       看一下2.6.31中定义的9个软中断:

enum

{

       HI_SOFTIRQ=0,

       TIMER_SOFTIRQ,

       NET_TX_SOFTIRQ,

       NET_RX_SOFTIRQ,

       BLOCK_SOFTIRQ,

       TASKLET_SOFTIRQ,

       SCHED_SOFTIRQ,

       HRTIMER_SOFTIRQ,

       RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

 

       NR_SOFTIRQS

};

       这些软中断从0开始索引来表示一种相对优先级。索引号小的优先级最高。

第一类软中断如NET_TX_SOFTIRQNET_RX_SOFTIRQ,对应于网络系统,其处理速度要求很高,那么这两个软中断对应的中断处理函数就唯一用于处理网络packet的收或发处理;

第二类软中断如HI_SOFTIRQ TASKLET_SOFTIRQ软中断,由未在软中断列出的其他硬件中断共享,其软中断处理函数是tasklet_hi_action()tasklet_action()函数。两者的唯一区别是其软中断优先级不同而已。这两个软中断处理函数是tasklet机制的核心,它会分别遍历tasklet_hi_vectasklet_vec链表,执行其中的所有允许执行的tasklet

3 tasklet

3.1 tasklet定义

       由动态链表tasklet_hi_vectasklet_vec构成,其数据结构定义在linux/interrupt.h

       struct tasklet_struct

{

       struct tasklet_struct *next;

       unsigned long state;

       atomic_t count;

       void (*func)(unsigned long);

       unsigned long data;

};

State有两种(2.6.31):

enum

{

       TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */

       TASKLET_STATE_RUN      /* Tasklet is running (SMP only) */

};

Counttasklet的计数器。如果它不为0,则tasklet被禁止,不允许执行;只有它为0时,tasklet才被激活,并且在stateTASKLET_STATE_SCHED时,该tasklet才能被执行。

Func就是tasklet对应得执行函数。

3.2 tasklet使用

       共分为三个步骤,分别为声明、激活、调度。

1)声明tasklet

       声明一个tasklet,其实就是定义一个tasklet_struct结构。可以选择静态或动态的创建一个tasklet

       静态创建可以使用linux/interrupt.h中定义的两个宏中的一个:

       #define DECLARE_TASKLET(name, func, data) \

struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

 

#define DECLARE_TASKLET_DISABLED(name, func, data) \

struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

这两个宏都会静态的创建一个tasklet_struct结构。区别在于引用计数器的初始值设置不同。前面一个设置计数器为0,该tasklet处于激活状态。另一个则把引用计数器设为1tasklet处于禁止状态。

动态创建时,可以使用kernel/softirq中的tasklet_init函数:

void tasklet_init(struct tasklet_struct *t,

                void (*func)(unsigned long), unsigned long data)

{

       t->next = NULL;

       t->state = 0;

       atomic_set(&t->count, 0);

       t->func = func;

       t->data = data;

}

 

2)激活或禁止tasklet

       针对于引用计数器,定义在linux/interrupt.h中:

       static inline void tasklet_enable(struct tasklet_struct *t)

static inline void tasklet_disable(struct tasklet_struct *t)

如果声明tasklet时就已经处于激活状态,那么就不需要重新激活了。

3)调度tasklet

       由在软中断中的分析可知,tasklet的执行依靠对tasklet_hi_vectasklet_vec链表的遍历。如何将我们自己声明的tasklet加入这两个链表呢?需要用户调度自己的tasklet。已经调度的tasklet就存放在tasklet_hi_vectasklet_vec链表中。

       tasklet_hi_schedule()tasklet_schedule函数接受一个tasklet_struct指针作为参数,对该tasklet进行调度,如果调度成功,则该tasklet就会存到tasklet_hi_vectasklet_vec链表。

 

4 总结

       软中断是一种下半部执行的机制,而tasklet是基于软中断实现的。增加自己的软中断是比较繁琐的,需要自己修改linux源码,比较繁琐。而Tasklet已经能够满足基本的下半部需求,所以基本上都使用tasklet机制。

阅读(1571) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~