Chinaunix首页 | 论坛 | 博客
  • 博客访问: 581989
  • 博文数量: 70
  • 博客积分: 3736
  • 博客等级: 中校
  • 技术积分: 1728
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-08 09:15
文章分类
文章存档

2014年(1)

2012年(21)

2011年(7)

2010年(28)

2009年(13)

分类: LINUX

2010-05-14 15:23:36

一.变量定义及其含义:

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) |
给主人留下些什么吧!~~