![Rank: 8](images/default/star_level3.gif) ![Rank: 8](images/default/star_level3.gif)
- UID
- 872238
|
![](http://images.eccn.com/silabs/silicon_chip_980x60_202203.jpg)
2. tq_timer的执行路径分析。
tq_timer是一个双链表结构任务队列,每项任务包含一个函数指针成员, 它通过run_task_queue每次将当中的所有任务(其实是一些函数指针)全部调用一次,然后清空队列, 最终的执行tq_timer的是在中断底半的tqueue_bh 中执行,如下:
void tqueue_bh(void)
{
run_task_queue(&tq_timer);
}
在void __init sched_init(void)当中初始化底半的向量如, tqueue_bh初始化在bh_base的TIMER_BH位置,bh_base为一结构很简单的数组,在什么位置调用什么样的了函数基本已经形成默认的习惯:
init_bh(TIMER_BH, timer_bh);
init_bh(TQUEUE_BH, tqueue_bh);
init_bh(IMMEDIATE_BH, immediate_bh);
看看init_bh相当于初始底半的服务程序,非常简单:
void init_bh(int nr, void (*routine)(void))
{
bh_base[nr] = routine;
mb();
}
最终真正的执行bh_base中保存的函数指针的,在bh_action()当中:
static void bh_action(unsigned long nr)
{
…
if (bh_base[nr])
bh_base[nr]();
…
}
关于这里所指出的bh_base, 我们在后面就直接称作bh,意即中断底半所做的事。
3. tq_timer实现所依赖的tasklet.
那么bh_action在什么时候执行呢?bh_action被初始化成bh_task_vec这32个tasklet调用的任务, 因此它的依赖机制是tasklet机制,后面将进行简单介绍。
void __init softirq_init()
{
int i;
for (i=0; i<32; i++)
tasklet_init(bh_task_vec+i, bh_action, i);
…
}
至此已经把任务队列的执行流程及原理分析完成,tasklet是须要激活的,这里我们先指出任务队列是如何激活的,在时钟中断的do_timer()当中会调用mark_bh(TIMER_BH), 来激时钟底半所依赖运行的tasklet,其中bh_task_vec的所有成员的函数指针全部指向bh_action.
static inline void mark_bh(int nr)
{
tasklet_hi_schedule(bh_task_vec+nr);
}
tasklet_hi_schedule的功能就是往tasklet当中加入一个新的tasklet.
4. tasklet的机制简单分析。
讲到tasklet,我们才与我们真正要讲的softirq最近了,因为目前在软中断当中有主要的应用就是tasklet,而且在所有32个软中断中仅有限的几个软中断如下:
enum{
HI_SOFTIRQ=0,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
TASKLET_SOFTIRQ
};
struct softirq_action{
void (*action)(struct softirq_action *);
void *data;
};
static struct softirq_action softirq_vec[32] __cacheline_aligned; //软中断的中断向量表,实为数组。
[1]. 初始化软中断向量。
我们这里所要讲的,就是HI_SOFTIRQ / TASKLET_SOFTIRQ 两项,据我理解这两项根本在实现机制上一样的,之所以分开两个名字叫主要是为了将不同的功能分开,就类似于虽然同是软中断,但是各处所完成的功能不一样,所以分在两个软中断完成, 后面我们仅取其中用于执行时钟底半的任务队列HI_SOFTIRQ为例进行讲解, 而且我们不讲及多个CPU情况下的tasklet相关机制, 这两项软中断的实始化如下:
void __init softirq_init()
{
…
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
open_softirq下所做的事相当简单, 即往软中断向量中赋值, 相当于硬中断当中的request_irq挂硬件中断:
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
[2]. 软中断中断服务程序
对于HI_SOFTIRQ , 相应的中断服务程序为tasklet_hi_action , 由上文所讲的初始化过程给出,这个函数目前完成的功能相当简单,它的任务就是遍历执行此中断所对应一个tasklet链表,
NR_CPUS= 1.
struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
[3]. 往软中断对应的tasklet链表中加入新的tasklet, 加在尾部。
void __tasklet_hi_schedule(struct tasklet_struct *t)
{
…
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
cpu_raise_softirq(cpu, HI_SOFTIRQ);
…
}
最重要的一点是,在安装了新的tasklet后,还必须将软中断设置为激活,告诉系统有软中断须要执行了,下面一点即提到系统如何检测是否有软中断须要处理:
#define __cpu_raise_softirq(cpu, nr) do { softirq_pending(cpu) |= 1UL 《 (nr); } while (0)
[4]. 软中断所依赖的执行机制。
讲到最后还没有指出软中断是如何触发执行的,其实很简单:
在系统处理所有硬中断信号时,他们的入口是统一的,在这个入口函数当中除了执行do_IRQ()完成硬件中断的处理之外,还会执行do_softirq()来检测是否有软中断须要执行,所以软中断所依赖的是硬件中断机制;
另外还有一个专门处理软中断内核线程ksoftirqd(),这个线程处理软中断级别是比较低的,他是一个无限LOOP不停的检测是否有软中断须要处理,如果没有则进行任务调度。
在do_softirq()中有如下的判断,以决定是否有软中断须要执行,如果没有就直接退出,在[3]中提到的激活软中断时,要将相应软中断位置1, 软中断有32个,因此一个整型数即可以表示32个软中断,即可判断有什么样的软中断须要处理,代码如下:
pending = softirq_pending(cpu);
if (pending) {
}
…
do { //检测32个软中断位标志中是否有为1的…
if (pending & 1)
h->action(h);
h++;
pending 》= 1;
} while (pending);
[4]. 软中断所依赖的执行时期问题。
之所以将这个问题单独列开来讲,是因为他特别的重要,上面我已经讲过了软中断是依赖硬中断触发执行的,但是产生如下疑问:
是不是一有硬中断发生就会触发软中断的执行?
软中断的执行会不会影响到系统的性能?
会不会影响到硬中断的处理效率?也就是说会不会导致在处理软中断时而引起硬中断无法及时响应呢?
再看do_softirq的代码当中有如下判断:
if (in_interrupt())
return;
这个条件就是能否进行软中断处理的关键条件,因此由此也可以了解到软中断是一种优先级低于硬中断的软性机制,具体来看看这个判断条件是什么:
/*Are we in an interrupt context? Either doing bottom half
* or hardware interrupt processing?*/
#define in_interrupt() ({ const int __cpu = smp_processor_id();
(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })
/* softirq.h is sensitive to the offsets of these fields */
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 */
} ____cacheline_aligned irq_cpustat_t;
#define irq_enter(cpu,irq) (local_irq_count(cpu)++)
#define irq_exit(cpu,irq) (local_irq_count(cpu)--) |
|