微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Linux 2.4.x内核软中断机制

Linux 2.4.x内核软中断机制

时间:04-06 来源: 点击:
本文从Linux内核几种软中断机制相互关系和发展沿革入手,分析了这些机制的实现方法,给出了它们的基本用法。

软中断概况

软中断是利用硬件中断的概念,用软件方式进行模拟,实现宏观上的异步执行效果。很多情况下,软中断和"信号"有些类似,同时,软中断又是和硬中断相对应的,"硬中断是外部设备对CPU的中断","软中断通常是硬中断服务程序对内核的中断","信号则是由内核(或其他进程)对某个进程的中断"(《Linux内核源代码情景分析》第三章)。软中断的一种典型应用就是所谓的"下半部"(bottom half),它的得名来自于将硬件中断处理分离成"上半部"和"下半部"两个阶段的机制:上半部在屏蔽中断的上下文中运行,用于完成关键性的处理动作;而下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。bottom half的应用也是激励内核发展出目前的软中断机制的原因,因此,我们先从bottom half的实现开始。

bottom half

在Linux内核中,bottom half通常用"bh"表示,最初用于在特权级较低的上下文中完成中断服务的非关键耗时动作,现在也用于一切可在低优先级的上下文中执行的异步动作。最早的bottom half实现是借用中断向量表的方式,在目前的2.4.x内核中仍然可以看到:

static void (*bh_base[32])(void); /* kernel/softirq.c */

系统如此定义了一个函数指针数组,共有32个函数指针,采用数组索引来访问,与此相对应的是一套函数:

void init_bh(int nr,void (*routine)(void));

为第nr个函数指针赋值为routine。

void remove_bh(int nr);

动作与init_bh()相反,卸下nr函数指针。

void mark_bh(int nr);

标志第nr个bottom half可执行了。

由于历史的原因,bh_base各个函数指针位置大多有了预定义的意义,在v2.4.2内核里有这样一个枚举:

enum { TIMER_BH = 0, TQUEUE_BH, DIGI_BH, SERIAL_BH, RISCOM8_BH, SPECIALIX_BH, AURORA_BH, ESP_BH, SCSI_BH, IMMEDIATE_BH, CYCLADES_BH, CM206_BH, JS_BH, MACSERIAL_BH, ISICOM_BH };

并约定某个驱动使用某个bottom half位置,比如串口中断就约定使用SERIAL_BH,现在我们用得多的主要是TIMER_BH、TQUEUE_BH和IMMEDIATE_BH,但语义已经很不一样了,因为整个bottom half的使用方式已经很不一样了,这三个函数仅仅是在接口上保持了向下兼容,在实现上一直都在随着内核的软中断机制在变。现在,在2.4.x内核里,它用的是tasklet机制。

task queue

在介绍tasklet之前,有必要先看看出现得更早一些的task queue机制。显而易见,原始的bottom half机制有几个很大的局限,最重要的一个就是个数限制在32个以内,随着系统硬件越来越多,软中断的应用范围越来越大,这个数目显然是不够用的,而且,每个bottom half上只能挂接一个函数,也是不够用的。因此,在2.0.x内核里,已经在用task queue(任务队列)的办法对其进行了扩充,这里使用的是2.4.2中的实现。

task queue是在系统队列数据结构的基础上建成的,以下即为task queue的数据结构,定义在include/linux/tqueue.h中:

struct tq_struct { struct list_head list; /* 链表结构 */ unsigned long sync; /* 初识为0,入队时原子的置1,以避免重复入队 */ void (*routine)(void *); /* 激活时调用的函数 */ void *data; /* routine(data) */};typedef struct list_head task_queue;

在使用时,按照下列步骤进行:

DECLARE_TASK_QUEUE(my_tqueue); /* 定义一个my_tqueue,实际上就是一个以tq_struct为元素的list_head队列 */ 说明并定义一个tq_struct变量my_task; queue_task(&my_task,&my_tqueue); /* 将my_task注册到my_tqueue中 */ run_task_queue(&my_tqueue); /* 在适当的时候手工启动my_tqueue */

大多数情况下,都没有必要调用DECLARE_TASK_QUEUE()定义自己的task queue,因为系统已经预定义了三个task queue: tq_timer,由时钟中断服务程序启

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top