Chinaunix首页 | 论坛 | 博客
  • 博客访问: 443497
  • 博文数量: 184
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 594
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-17 16:24
个人简介

我是一只小小鸟

文章分类

全部博文(184)

文章存档

2016年(1)

2015年(55)

2014年(127)

2013年(1)

分类: 嵌入式

2014-05-23 14:53:16

原文地址:linux 内核tasklet详解 作者:guanglongxishui

本文内核版本为2.6.32

软中断被执行的优先级要高于内核线程。硬中断是可以抢占内核线程的,硬中断退出时会立即执行软中断。这时软中断执行程序是运行在中断上下文的。如果软中断执行程序在指定时间内没处理完,就会挂起来等下次下次被执行。下次被执行可以是另一个硬中断退出时在中断上下文中执行,也可以是在特殊的内核线程ksoftirq被调度到来执行,这时是运行在线程上下文的。

总体来说,软中断执行程序被执行的机会会比普通线程要多。所以一些要优先并需要及时处理的工作可以交给软中断来处理。但linux 实现了软中断,但对内核模块开发的人员来说,内核并没有直接提供使用软中断的API。内核提供了tasklet 来给内核模块开发人员来用。

tasklet是在软中断HI_SOFTIRQ和TASKLET_SOFTIRQ基础上实现的。

初始化:
在start_kernel()里调用softirq_init()初始化这两个软中断。

点击(此处)折叠或打开

  1. open_softirq(TASKLET_SOFTIRQ, tasklet_action,NULL);
  2. open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
数据结构

每个tasklet 由数据结构tasklet_struct 代表。

struct tasklet_struct
{
    struct tasklet_struct *next;
    unsigned long state;     //tasklet状态。
    atomic_t count;         //锁计数器
    void (*func)(unsigned long);  //tasklet处理函数。
    unsigned long data;      //处理函数需要的参数。
};

全局数组tasklet_vec[NR_CPU] 和 tasklet_hi_vec[NR_CPU] 。数组元素是一个tasklet_head元素,是tasklet_struct 链表头。数组下标对应每个cpu ID.即数组保存了每个cpu上的tasklet_struct 链表。

tasklet 的state状态字段有如下状态:
TASKLET_STATE_SCHED 表示tasklet已经插入到tasklet_vec 或 tasklet_hi_vec数组中的一个链表上了。
TASKLET_STATE_RUN 表示tasklet正在被执行。

static inline int tasklet_trylock(struct tasklet_struct *t)
{
    return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}

static inline void tasklet_unlock(struct tasklet_struct *t)
{
    smp_mb__before_clear_bit();
    clear_bit(TASKLET_STATE_RUN, &(t)->state);
}

调用tasklet_init()来定义一个tasklet.

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


禁止tasklet:
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
    /*禁止tasklet后立即返回*/
    atomic_inc(&t->count);   //增加tasklet的锁计数器。
    smp_mb__after_atomic_inc();
}


static inline void tasklet_unlock_wait(struct tasklet_struct *t)
{
    /*直到等到tasklet执行完毕返回*/
    while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
}

static inline void tasklet_disable(struct tasklet_struct *t)
{
    /*禁止tasklet并且直到tasklet执行完毕后返回*/
    tasklet_disable_nosync(t);
    tasklet_unlock_wait(t);
    smp_mb();
}


激活tasklet:
static inline void tasklet_enable(struct tasklet_struct *t)
{
    smp_mb__before_atomic_dec();
    /*递减tasklet的锁计数器*/
    atomic_dec(&t->count);
}


调度tasklet
根据tasklet的优先级调用tasklet_schedule() 或tasklet_hi_schedule().

static inline voidtasklet_schedule(struct tasklet_struct *t)
{
    /*如果tasklet没被调度过,即没被插入tasklet_vec相应的链表 ,调度*/
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
    {
        __tasklet_schedule(t);
    }
}


void fastcall__tasklet_schedule(struct tasklet_struct *t)
{
    unsigned long flags;
    /*保存中断状态寄存器并关闭本地CPU的中断*/
    local_irq_save(flags);

    /*把tasklet插入本地CPU的tasklet_vec中对应的链表里*/
    t->next = __get_cpu_var(tasklet_vec).list;
    __get_cpu_var(tasklet_vec).list = t;
   
   /*把本地CPU的软中断TASKLET_SOFTIRQ位标记为挂起*/
    raise_softirq_irqoff(TASKLET_SOFTIRQ);

    /*恢复中断状态寄存器并开本地CPU中断*/

    local_irq_restore(flags);
}


软中断TASKLET_SOFTIRQ的处理函数如下:

点击(此处)折叠或打开

  1. static void tasklet_action(struct softirq_action *a)
  2. {
  3.     struct tasklet_struct *list;
  4.     /*保存中断状态寄存器并关闭本地CPU的中断*/

  5.     local_irq_disable();

  6.     /*取得tasklet_vec数组中本地CPU的tasklet链表,并存入临时变量中*/
  7.     list = __get_cpu_var(tasklet_vec).list;

  8.     /*清空tasklet_vec数组中本地CPU的tasklet链表*/
  9.     __get_cpu_var(tasklet_vec).list = NULL;

  10.     /*恢复中断状态寄存器并开本地CPU中断*/
  11.     local_irq_enable();

  12.     /*循环执行tasklet链表上每个tasklet的处理函数*/
  13.     while (list)
  14.     {
  15.         /*从链表上摘下一个tasklet*/
  16.         struct tasklet_struct *t = list;
  17.         list = list->next;
  18.     
  19.        /*如果tasklet没在被执行,执行,设置tasklet 的state字段为RUNNING状态*/
  20.         if (tasklet_trylock(t))
  21.         {
  22.             /*如果tasklet的锁计数器为0,执行*/
  23.             if (!atomic_read(&t->count))
  24.             {
  25.                 /*清除tasklet的SCHED状态*/
  26.                 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
  27.                     BUG();
  28.                 /*执行tasklet的处理函数*/
  29.                 t->func(t->data);
  30.                 /*清除tasklet 的state字段的RUNNING状态,继续处理链表上的下一个tasklet*/
  31.                 tasklet_unlock(t);
  32.                 continue;
  33.             }

  34.             /*如果tasklet 的锁计数器不为0,表示tasklet被禁用,清除state字段的RUNNING状态*/
  35.             tasklet_unlock(t);
  36.         }

  37.         /*关闭本地CPU中断,并把以上没被处理的tasklet重新挂到tasklet_vec数组中对应本地CPU上的链表上*/
  38.         local_irq_disable();
  39.         t->next = __get_cpu_var(tasklet_vec).list;
  40.         __get_cpu_var(tasklet_vec).list = t;
  41.        /*把本地CPU上的TASKLET_SOFTIRQ标记为挂起,并使能中断*/
  42.        __raise_softirq_irqoff(TASKLET_SOFTIRQ);
  43.        local_irq_enable();
  44.     }
  45. }


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