一.变量定义及其含义:
1.kernel/softirq.c
struct tasklet_head
{
struct tasklet_struct *list;
};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL };
2.数据结构irq_cpustat_t来描述一个CPU的中断统计信息,其中就有用于触发软中断的成员变量
include/asm/hardirq.h
typedef struct {
unsigned int __softirq_pending; //
unsigned int local_timer_irqs;
} ____cacheline_aligned irq_cpustat_t;
结构中每一个成员都是一个32位的无符号整数。
__softirq_active变量:32位的无符号整数,表示软中断向量0~31的状态。
__softirq_active变量的作用是记录某个cpu上是否有软中断发生
如果bit[i](0≤i≤31)为1,则表示软中断向量i在某个CPU上已经被触发而处于active状态;为0表示处于非活跃状态。
__softirq_active变量和硬件中断里边的中断标志寄存器很像,但有不同,不同之处是什么?[问题]
kernel/softirq.c
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
4.
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
state:只用了bit[0],bit[1]和bit[2],既然只用了3 bits,为什么要将state定义为unsigned long?[问题]
bit[1]=1表示这个tasklet当前正在某个CPU上被执行,它仅对SMP系统才有意义,其作用就是为了防止多个CPU同时执行一个tasklet的情形出现;
bit[0]=1表示这个tasklet已经被调度去等待执行了
count:
只有当count等于0时,tasklet代码段才能执行,也即此时tasklet是被使能的;如果count非零,则这个tasklet是被禁止的
二、软中断的注册和tasklet的执行过程
init/main.c执行softirq_init
void __init softirq_init(void)
{
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
tasklet_action可以在不同cpu上并行运行
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = NULL; //理论上当前CPU将不再有tasklet需要执行
local_irq_enable();
__tasklet_action(a, list);
}
static void
__tasklet_action(struct softirq_action *a, struct tasklet_struct *list)
{
int loops = 1000000;
while (list) {
struct tasklet_struct *t = list; //t摘除头部,list保存剩余链表
list = list->next;
if (!tasklet_trylock(t)) { //如果当前没有任何其他CPU正在执行这个tasklet(t->state的TASKLET_STATE_RUN位为0)
//则将t->state的TASKLET_STATE_RUN位置1,继续;否则,直接退出
WARN_ON(1);
continue;
}
t->next = NULL;
if (unlikely(atomic_read(&t->count))) { //前面我们说了,只有当count等于0时,tasklet代码段才能执行
out_disabled:
/* implicit unlock: */
wmb();
t->state = TASKLET_STATEF_PENDING;
continue;
}
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) //如果tasklet正在等待调度(t->state的TASKLET_STATE_SCHED位为1),
//则将t->state的TASKLET_STATE_SCHED位清0,继续;否则,直接退出
WARN_ON(1);
again:
t->func(t->data); //执行在tasklet中注册的函数
...
}
三、tasklet的触发
include/linux/interrupt.h
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
假如tasklet还没有被调度(TASKLET_STATE_SCHED位置0),那么设置TASKLET_STATE_SCHED表示被调度,然后调用__tasklet_schedule;
假如tasklet已经被调度(TASKLET_STATE_SCHED置1),什么都不作,返回
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
__tasklet_common_schedule(t, &__get_cpu_var(tasklet_vec), TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
__tasklet_common_schedule(struct tasklet_struct *t, struct tasklet_head *head, unsigned int nr)
{
if (tasklet_trylock(t)) { //tasklet的都被执行了,那调度还有什么意义呢?
WARN_ON(t->next != NULL);
t->next = head->list; //用头插方式将此tasklet插入到此cpu的tasklet_head队列
head->list = t;
raise_softirq_irqoff(nr); //在当前CPU上触发软中断请求TASKLET_SOFTIRQ,其实就是设置irq_state[i].__softirq_pending变量的第TASKLET_SOFTIRQ位为1(i为当前cpu号)
tasklet_unlock(t);
}
}
通过调用tasklet_schedule,就相当于触发了软中断,那么剩下的工作,就是执行了,那软中断在什么地方会被执行呢?
四、软中断的执行时机
a.从一个硬件中断代码处返回
b.在ksoftirqd内核线程中
c.显式检查和执行待处理的软中断的代码中
但不论哪种方式唤起,软中断都要在do_softirq()中执行
每个处理器都有一个叫做ksoftirqd的线程,n号处理器的这个线程就叫作ksoftirqd/n,这样do_softirq就可能会在不同的cpu上执行
do_softirq的实现如下:
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
if (in_interrupt())
return;
local_irq_save(flags);
pending = local_softirq_pending();//检查irq_state[i].__softirq_pending变量(i为当前cpu号)
//前面我们有讲过,__softirq_active变量的作用相当于硬件中断里边的中断标志寄存器
if (pending)
__do_softirq(); //假如为0,则说明没有软件中断被触发
local_irq_restore(flags);
}
asmlinkage void ___do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int cpu;
pending = local_softirq_pending();
cpu = smp_processor_id();
restart:
h = softirq_vec;
do {
if (pending & 1)
h->action(h);//对于tasklet,则执行tasklet_action
}
h++;
pending >>= 1;
} while (pending);
...
}
总结:
1.softirq机制的设计与实现中自始自终都贯彻了一个思想:“谁触发,谁执行”(Who marks,Who runs),也即触发软中断的那个CPU负责执行它所触发的软中断,而且每个CPU都由它自己的软中断触发与控制机制。这个设计思想也使得softirq 机制充分利用了SMP系统的性能和特点。
2.tasklet也建立在软中断之上,所以它也遵从“谁触发,谁执行”的设计理念,这意味着同一个tasklet只能在同一个cpu上执行,而不同的tasklet则可以在不同的cpu上执行
代码版本:
linux2.6.16
xin.jin
阅读(1567) | 评论(0) | 转发(0) |