Chinaunix首页 | 论坛 | 博客
  • 博客访问: 156591
  • 博文数量: 49
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 445
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-16 23:55
文章分类
文章存档

2009年(49)

我的朋友

分类: LINUX

2009-05-25 12:17:01

十五 softirqs and tasklets

linux2.6里面用两种功能来给非紧急中断使用,软中断和tasklet。都叫defferable functions

  softirqstasklets是严格相关的,因为tasklet是建立在softirq之上的。

  软中断和tasklet的不同

  1软中断必须在编译的时候就被分配好,但是tasklet可以在运行的时候被初始化和分配。(比如在添加一个kernel module的时候)

  2 softirq可以在不同的CPU上同时运行一个type的。但是tasklet是被串行化了的,不能同时在不同的CPU上运行同一个类型的。(tasklet不用解决可重入的问题,所以对device developer比较好用)

  defferable functions里面可以有的4中操作:

  1 初始化:在kernel初始化的时候,或者module被加载的时候。

  2 活动性(activation

  3 masking 掩码,用来屏蔽不需要被执行的defferable function

  4 执行

十六 softirqs

  表示softirqs的主要数据结构是softirq_vec[]数组。里面有32softirq_action数据结构。

  Softirq_action里面有个指向softirq function的指针,还有一个指向softirq function可能被用到的数据结构。

  Open_softirq()函数用来初始化软中断。它有三个传递参数,index(优先权),指向softirq function的指针,和softirq function要用到的数据结构。

  Raise_softirq()用来激活softirq

  do_softirq()函数。

     这个函数做一些实现检查,然后调用__do_softirq()来轮询需要被操作的softirqs

十七 tasklets

  I/O设备中,比较常用taskletTasklet是建立在两个softirq的名字为HI_SOFTIRQTASKLET_SOFTIRQ之上的。

 

Tasklet是被存在taskllet_vec[]tasklet_hi_vec[]上面的,每个tasklet有一个数据结构,tasklet_struct

{

  Next; state; count; func; data;

}

具体意义参加p178

一个使用tasklet写设备驱动的具体例子:1.分配task_struct,并用tasklet_init()初始化它。2.这个函数的参数有:tasklet descriptor; tasklet function的地址; 和一些可选择项

  要激活一个tasklet,必须调用tasklet_schedule()或者tasklet_hi_schedule()

  Tasklet是怎样被执行的:通过do_softirq()来执行softirq function,这就可以调用tasklet

具体的过程参加p179

十八 工作队列 work queue 

  2.6中引入work queue来代替2.4里面的task queue

  Work queuedefferable function的不同:

  就是后者运行在interrupt context下面,而前者运行在process context下面。这样后者就可以被堵塞(blocked),process switch不可能在interrupt context下面执行。

  Work queue里面的函数是在内核线程(kernel thread)下面被执行的,所以没有USER MODE address space可以进入,这个是不是说明内核线程没有用户空间?

  关于工作队列的具体内容参看p181

十九 在中断和异常中返回

  两个返回在大体上差不多。

  ret_from_exception() and ret_from_intr()

1有内核控制路径的嵌套么?(没有的话返回)2需要有其他工作被做么?(没有的话返回) 有的话将工作挂起。3需要sche么?4work_notifysig,发送信号。

具体的部分参看p185以后的部分。

 

这个部分的内容太多了。。。taskletirq的具体应用还是不怎么清楚。

某位仁兄的博客里面的技术文章引用:

http://man.lupaworld.com/content/develop/joyfire/kernel/2.html#I368

http://www.cnblogs.com/huqingyu/archive/2005/03/07/114599.html

提出softirq的机制的目的和老版本的底半部分的目的是一致的,都是将某个中断处理的一部分任务延迟到后面去执行。

Linux内核中一共可以有32softirq,每个softirq实际上就是指向一个函数。当内核执行softirq(do_softirq),就对这32softirq进行轮询:

(1)是否该softirq被定义了,并且允许被执行?

(2)是否激活了(也就是以前有中断要求它执行)?

如果得到肯定的答复,那么就执行这个softirq指向的函数。

值得一提的是,无论有多少个CPU,内核一共只有32个公共的softirq,但是每个CPU可以执行不同的softirq,可以禁止/起用不同的softirq,可以激活不同的softirq,因此,可以说,所有CPU有相同的例程,但是

每个CPU却有自己完全独立的实例。

(1)的判断是通过考察irq_stat[ cpu ].mask相应的位得到的。这里面的cpu指的是当前指令所在的cpu.在一开始,softirq被定义时,所有的cpu的掩码mask都是一样的。但是在实际运行中,每个cpu上运行的程序可以根据自己的需要调整。

(2)的判断是通过考察irq_stat[ cpu ].active相应的位得到的.

虽然原则上可以任意定义每个softirq的函数,Linux内核为了进一步加强延迟中断功能,提出了tasklet的机制。tasklet实际上也就是一个函数。在第0softirq的处理函数tasklet_hi_action中,我们可以看到,当执行这个函数的时候,会依次执行一个链表上所有的tasklet.

我们大致上可以把softirq的机制概括成:

内核依次对32softirq轮询,如果遇到一个可以执行并且需要的softirq,就执行对应的函数,这些函数有可能又会执行一个函数队列。当执行完这个函数队列后,才会继续询问下一个softirq对应的函数。

 

 挂上一个软中断

void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
 unsigned long flags;
 int i;
 spin_lock_irqsave(&softirq_mask_lock, flags);
 softirq_vec[nr].data = data;
 softirq_vec[nr].action = action;
 for (i=0; i    softirq_mask(i) |= (1< spin_unlock_irqrestore(&softirq_mask_lock, flags);
}

其中对每个CPUsoftirq_mask都标注一下,表明这个softirq被定义了。

tasklet

在这个32softirq中,有的softirq的函数会依次执行一个队列中的tasklet

tasklet其实就是一个函数。它的结构如下:

struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};

next 用于将tasklet串成一个队列

state 表示一些状态,后面详细讨论

count 用来禁用(count = 1 )或者启用( count = 0 )这个tasklet.因为一旦一个tasklet被挂到队列里,如果没有这个机制,它就一定会被执行。 这个count算是一个事后补救措施,万一挂上了不想执行,就可以把它置1

func 即为所要执行的函数。

data 由于可能多个tasklet调用公用函数,因此用data可以区分不同tasklet.

如何将一个tasklet挂上

首先要初始化一个tasklet,填上相应的参数

void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->func = func;
t->data = data;
t->state = 0;
atomic_set(&t->count, 0);
}


然后调用schedule函数,注意,下面的函数仅仅是将这个tasklet挂到 TASKLET_SOFTIRQ对应的软中断所执行的tasklet队列上去, 事实上,还有其它的软中断,比如HI_SOFTIRQ,会执行其它的tasklet队列,如果要挂上,那么就要调用tasklet_hi_schedule(). 如果你自己写的softirq执行一个tasklet队列,那么你需要自己写类似下面的函数。

static inline void tasklet_schedule(struct tasklet_struct *t)
{
 if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
 {
  int cpu = smp_processor_id();
  unsigned long flags;
  local_irq_save(flags);
  /**/ t->next = tasklet_vec[cpu].list;
  /**/ tasklet_vec[cpu].list = t;
  __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
  local_irq_restore(flags);
 }
}

这个函数中/**/标注的句子用来挂接上tasklet,

__cpu_raise_softirq用来激活TASKLET_SOFTIRQ,这样,下次执行do_softirq就会执行这个TASKLET_SOFTIRQ软中断了

__cpu_raise_softirq定义如下:

static inline void __cpu_raise_softirq(int cpu, int nr)
{
softirq_active(cpu) |= (1<}

tasklet的运行方式

我们以tasklet_action为例,来说明tasklet运行机制。事实上,还有一个函数tasklet_hi_action同样也运行tasklet队列。

首先值得注意的是,我们前面提到过,所有的cpu共用32softirq,但是同一个softirq在不同的cpu上执行的数据是独立的,基于这个原则,tasklet_vec对每个cpu都有一个,每个cpu都运行自己的tasklet队列。

 

当执行一个tasklet队列时,内核将这个队列摘下来,以list为队列头,然后从list的下一个开始依次执行。这样做达到什么效果呢?在执行这个队列时,这个队列的结构是静止的,如果在运行期间,有中断产生,并且往这个队列里添加tasklet的话,将填加到tasklet_vec[cpu].list中, 注意这个时候,这个队列里的任何tasklet都不会被执行,被执行的是list接管的队列。

软中断调用时机

最直接的调用:

当硬中断执行完后,迅速调用do_softirq来执行软中断(见下面的代码),这样,被硬中断标注的软中断能得以迅速执行。当然,不是每次调用都成功的,见前面关于重入的帖子。

-----------------------------------------------------

asmlinkage unsigned int do_IRQ(struct pt_regs regs)

{

... ...

if (softirq_active(cpu) & softirq_mask(cpu))

do_softirq();

}

-----------------------------------------------------

还有,不是每个被标注的软中断都能在这次陷入内核的部分中完成,可能会延迟到下次中断。

其它地方的调用:

entry.S中有一个调用点:

handle_softirq:

call SYMBOL_NAME(do_softirq)

jmp ret_from_intr

有两处调用它,一处是当系统调用处理完后:

ENTRY(ret_from_sys_call)

#ifdef CONFIG_SMP
  movl processor(%ebx),%eax
  shll $CONFIG_X86_L1_CACHE_SHIFT,%eax
  movl SYMBOL_NAME(irq_stat)(,%eax),%ecx # softirq_active
  testl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx # softirq_mask
#else
  movl SYMBOL_NAME(irq_stat),%ecx # softirq_active
  testl SYMBOL_NAME(irq_stat)+4,%ecx # softirq_mask
#endif

jne handle_softirq
一处是当异常处理完后:

注意其中的irq_stat, irq_stat +4 对应的就是字段 activemask

既然我们每次调用完硬中断后都马上调用软中断,为什么还要在这里调用呢?

原因可能都多方面的:

(1)在系统调用或者异常处理中同样可以标注软中断,这样它们在返回前就能得以迅速执行

(2)前面提到,有些软中断要延迟到下次陷入内核才能执行,系统调用和异常都陷入内核,所以可以尽早的把软中断处理掉

(3)如果在异常或者系统调用中发生中断,那么前面提到,可能还会有一些软中断没有处理,在这两个地方做一个补救工作,尽量避免到下次陷入内核才处理这些软中断。

另外,在切换前也调用。

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