linux软中断与tasklet

来源:互联网 发布:矩阵计算 第4版 pdf 编辑:程序博客网 时间:2024/04/29 10:17

原文地址:http://blog.csdn.net/rockrockwu/article/details/7315677




软中断

软中断由

struct softirq_action{

Void(*action)(struct softirq_action *)


表示。并且当前内核中的软中断总数固定为32个,由数组Static struct softirq_actionsoftirq_vec[NR_SOFTIRQS]来表示。

目前只用到了其中的9个。包括定时器、网络、tasklet等。

软中断的执行是通过do_softirq来查询数组softirq_vec[NR_SOFTIRQS]对应的位图,查看是否有软中断挂起,

有则将已挂起的软中断通过:

h=softirq_vec

h->action(h)

来执行对应的软中断的action函数。所以,只要调用do_softirq都会查询软中断,并处理挂起的软中断。

一般do_softirq会在下列地方会执行:

1.      硬件中断返回函数,在硬件中断的返回函数irq_exit()会调用do_softirq(通过函数do_softirq的包装函数

invoke_softirq()间接访问)

2.      ksoftirqd内核线程

3.      显式调用do_softirq处,例如网络驱动中等

软中断的使用

首先,需使用新的软中断,则需用户在内核中分配新的索引

然后,通过open_softirq()函数注册一个软中断的处理函数,

例如open_softirq(TASKLET_SOFTIRQ,tasklet_action),其中tasklet_action函数即为软中断处理函数。

最后,需要在别的地方通过raise_softirq(TASKLET_SOFTIRQ)来触发软中断。

以使处理器择机尽早通过do_softirq来执行tasklet_action函数。假如在中断中使用了tasklet,则在硬件中断

退出前会有机会执行。

此处说明下ksoftirqd线程,该线程通过是early_initcall(spawn_ksoftirqd)

spawn_ksoftirqd-> cpu_callback –>kthread_create创建。

假如raise_softirq的步骤不是在中断处理函数中完成的(通过tasklet_schedule会执行raise_softirq操作),

或者raise_softirq之后一直未产生硬件中断,则软中断最后的执行是由调用了do_softirq函数的此线程来完成的,

或者如网卡驱动中直接显示调用do_softirq函数。值得说明的是,正是由于在硬件中断退出前会执行do_softirq,

使得采用tasklet的中断下半部性能很好。因为加入系统负荷不重,则tasklet会被立即执行。但其执行绝对不会晚于

下个内核时间片。因为中断退出后就会执行do_softirq,内核时间片采用的是定时器中断,在其中断处理函数退出前

会调用do_softirq,所以tasklet的执行时间比较有保障,其性能比同样用于中断下半部的工作队列高很多。

但是,假如是由ksoftirqd来处理的其他类型软中断性能可能就会差一点。

tasklet软中断

tasklet是在软中断TASKLET_SOFTIRQ的基础上实现的重要步骤如下:

首先,tasklet可通过DECLARE_TASKLET(my_tasklet,my_tasklet_handler,dev)

宏来初始化一个tasklet_struct。此处的task_handler为tasklet的处理函数。

其次,通过tasklet_schedule()将tasklet调度,以便有机会尽早的运行。

其中应该将第二步放在中断处理函数中,这样执行完中断处理函数后就有立即有

机会执行tasklet了。

tasklet和软中断的关系

首先系统在初始化的时候在start_kernel函数中通过调用softirq_init()函数,

在softirq_init()函数中通过调用open_softirq(TASKLET_SOFTIRQ,tasklet_action);

来注册一个软中断,其处理函数为tasklet_action。注册之后还需调用raise_softirq函数

来告诉内核TASKLET_SOFTIRQ软中断已挂起,以使处理器可以尽早处理该型软中断。

挂起的操作通过tasklet_schedule()函数间接调用raise_softirq()函数来实现

void __tasklet_schedule(structtasklet_struct *t)

{

       unsignedlong flags;

 

       local_irq_save(flags);

       t->next= NULL;

       *__get_cpu_var(tasklet_vec).tail= t;

       __get_cpu_var(tasklet_vec).tail= &(t->next);

       raise_softirq_irqoff(TASKLET_SOFTIRQ);

       local_irq_restore(flags);

}

此后则等待中断处理函数退出后调用do_softirq来执行TASKLET_SOFTIRQ软中断的处理函数asklet_action。

static void tasklet_action(structsoftirq_action *a)

{

       structtasklet_struct *list;

       local_irq_disable();

       list= __get_cpu_var(tasklet_vec).head;

……

       while(list) {

              structtasklet_struct *t = list;

              list= list->next;

              if(tasklet_trylock(t)) {

                     if(!atomic_read(&t->count)) {

                            if(!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))

                                   BUG();

                            t->func(t->data);

                     …...

       }

可以发现在tasklet_action函数中,会获取tasklet链表,然后依次

轮训已注册的tasklet处理函数。例如之前通过DECLAR_TASKLET宏注册的

my_tasklet_handler函数。

所以最终在驱动中使用tasklet需要的工作是:

1.初始化一个tasklet_struct结构,并注册对应的tasklet处理函数。

2.tasklet处理函数的实现

内核定时器

内核的定时器是通过定时器软中断实现的。

内核部分:

1.注册软中断:在内核启动时,在start_kernel函数中通过调用init_timers->open_softirq(TIMER_SOFTIRQ, run_timer_softirq)

实现注册TIMER_SOFTIRQ软中断,其中run_timer_softirq函数为软中断处理函数。

2.中断挂起:timer_interrupt->update_process_times->run_local_timers->raise_softirq(TIMER_SOFTIRQ);

其中timer_interrupt函数为定时器中断处理函数

驱动部分:

1.初始化一个timer_list,特别是初始化其中的func函数指针

2.func函数的实现,此为内核定时器中断处理函数。即定时时间到后,会调用此函数。

具体执行

在内核时间片到,定时器硬件中断退出时,会调用do_softirq函数。在此函数中先调用软中断处理函数run_timer_softirq,在此处理

函数中查看和修改内核定时器链表上的定时器。若定时时间到,则通过timer->func调用定时器处理函数。


0 0
原创粉丝点击