在具体介绍软中断处理机制之前,我们先介绍一下相关的数据结构,这些数据结构大部分都在/includee/linux/interrupt.h中
1.与软中断相关的数据结构
软中断本身是一种机制,同时也是一种基本框架。在这个框架中,既包含了bh机制,也包含了tasklet机制
(1) 内核定义的软中断
enum
{
HI_SOFTIRQ=0,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
TASKLET_SOFTIRQ
};
内核中用枚举类型定义了四种类型的软中断,其中NET_TX_SOFTIRQ和NET_RX_SOFTIRQ两个软中断是专为网络操作而设的,而HI_SOFTIRQ和TASKLET_SOFTIRQ是针对bh和tasklet而设的软中断。编码的作者在源码注释中曾提到,一般情况下,不要再分配新的软中断。
(2)软中断向量
struct softirq_action
{
void
(*action)(struct softirq_action *);
void
*data;
}
static struct softirq_action softirq_vec[32]
__cacheline_aligned;
从定义可以看出,内核定义了32个软中断向量,每个向量指向一个函数,但实际上,内核目前只定义了上面的四个软中断,而我们后面主要用到的为HI_SOFTIRQ和TASKLET_SOFTIRQ两个软中断。
(3)软中断控制/状态结构
softirq_vec[]是个全局量,系统中每个CPU所看到的是同一个数组。但是,每个CPU各有其自己的“软中断控制/状态”结构,这些数据结构形成一个以CPU编号为下标的数组irq_stat[](定义在include/i386/hardirq.h中)
typedef struct {
unsigned int __softirq_pending;
unsigned int __local_irq_count;
unsigned int
__local_bh_count;
unsigned int __syscall_count;
struct
task_struct * __ksoftirqd_task; /* waitqueue is too large */
unsigned int __nmi_count; /* arch
dependent */
} ____cacheline_aligned irq_cpustat_t;
irq_cpustat_t irq_stat[NR_CPUS];
irq_stat[]数组也是一个全局量,但是各个CPU可以按其自身的编号访问相应的域。于是,内核定义了如下宏(在include/linux/irq_cpustat.h中):
#ifdef CONFIG_SMP
#define __IRQ_STAT(cpu, member)
(irq_stat[cpu].member)
#else
#define __IRQ_STAT(cpu, member)
((void)(cpu), irq_stat[0].member)
#endif
/* arch independent irq_stat fields */
#define softirq_pending(cpu) __IRQ_STAT((cpu),
__softirq_pending)
#define local_irq_count(cpu) __IRQ_STAT((cpu),
__local_irq_count)
#define local_bh_count(cpu) __IRQ_STAT((cpu),
__local_bh_count)
#define syscall_count(cpu)
__IRQ_STAT((cpu), __syscall_count)
#define ksoftirqd_task(cpu) __IRQ_STAT((cpu),
__ksoftirqd_task)
/*
arch dependent irq_stat fields */
#define nmi_count(cpu)
__IRQ_STAT((cpu), __nmi_count)
/* i386, ia64 */
2.与tasklet相关的数据结构
与bh函数相比,tasklet是“多序”的bh函数。内核中用tasklet_task来定义一个tasklet:
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
从定义可以看出,tasklet_struct是一个链表结构,结构中的函数指针func指向其服务程序。内核中还定义了一个以CPU编号为下标的数组tasklet_vec[]和tasklet_hi_vec[]:
struct tasklet_head
{
struct tasklet_struct *list;
} __attribute__ ((__aligned__(SMP_CACHE_BYTES)));
extern struct tasklet_head
tasklet_vec[NR_CPUS];
extern struct tasklet_head
tasklet_hi_vec[NR_CPUS];
这两个数组都是tasklet_head结构数组,每个tasklet_head结构就是一个tasklet_struct结构的队列头。
3.与bh相关的数据结构
前面我们提到,bh建立在tasklet之上,更具体地说,对一个bh的描述也是tasklet_struct结构,只不过执行机制有所不同。因为在不同的CPU上可以同时执行不同的tasklet,而任何时刻,即使在多个CPU上,也只能有一个bh函数执行。
(1)
bh的类型
enum {
TIMER_BH = 0, /* 定时器 */
TQUEUE_BH, /* 周期性任务队列 */
DIGI_BH, /*
DigiBoard PC/Xe */
SERIAL_BH, /* 串行接口 */
RISCOM8_BH, /*
RISCom/8 */
SPECIALIX_BH, /* Specialix IO8+ */
AURORA_BH, /*
Aurora多端口卡(SPARC)*/
ESP_BH, /*
Hayes ESP 串行卡 */
SCSI_BH, /*
SCSI接口*/
IMMEDIATE_BH, /* 立即任务队列*/
CYCLADES_BH, /* Cyclades Cyclom-Y
串行多端口 */
CM206_BH, /*
CD-ROM Philips/LMS cm206磁盘 */
JS_BH, /*
游戏杆(PC IBM)*/
MACSERIAL_BH, /* Power Macintosh 的串行端口 */
ISICOM_BH /*
MultiTech的ISI卡*/
};
在给出bh定义的同时,我们也给出了解释。从定义中可以看出,有些bh与硬件设备相关,但这些硬件设备未必装在系统中,或者仅仅是针对IBM PC兼容机之外的某些平台。
(2) bh的组织结构
在2.4以前的版本中,把所有的bh用一个bh_base[]数组组织在一起,数组的每个元素指向一个bh函数:
static void
(*bh_base[32])(void);
2.4版中保留了上面这种定义形式,但又定义了另外一种形式:
struct
tasklet_struct bh_task_vec[32];
这也是一个有32个元素的数组,但数组的每个元素是一个tasklet_struct结构,数组的下标就是上面定义的枚举类型中的序号。