• 内核源码分析之tasklet(基于3.16-rc4)


    tasklet是在HI_SOFTIRQ和TASKLET_SOFTIRQ两个软中断的基础上实现的(它们是在同一个源文件中实现,由此可见它们的关系密切程度),它的数据结构和软中断比较相似,这篇博文将分析tasklet的初始化过程。

    1.和tasklet相关的数据结构

    tasklet_vec和tasklet_hi_vec数组(kernel/softirq.c)

    1 static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
    2 static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);

    第1行中为所有cpu创建了名为tasklet_vec数组,每个元素和一个cpu相关联,数组元素类型为struct tasklet_head。第2行和第1行类似,创建出的tasklet_hi_vec数组存放的是每个cpu上优先级更高的tasklet小任务。下面看下struct tasklet_head结构体(kernel/softirq.c)。

    1 struct tasklet_head {
    2     struct tasklet_struct *head;
    3     struct tasklet_struct **tail;
    4 };

    内核会为每个cpu分配一个这样的结构体变量,从而每个cpu都拥有一个小任务函数的链表。再看下结构体(kernel/softirq.c)。

    1 struct tasklet_struct
    2 {
    3     struct tasklet_struct *next;
    4     unsigned long state;
    5     atomic_t count;
    6     void (*func)(unsigned long);
    7     unsigned long data;
    8 };

    第1行next指向小任务链表下一个节点,第6行func变量指向了小任务函数。

    2.tasklet初始化

    小任务是基于HI_SOFTIRQ和TASKLET_SOFTIRQ两个软中断来实现的。HI_SOFTIRQ所对应的软中断函数是tasklet_action,如下(kernel/softirq.c):

     1 static void tasklet_action(struct softirq_action *a)
     2 {
     3     struct tasklet_struct *list;
     4 
     5     local_irq_disable();
     6     list = __this_cpu_read(tasklet_vec.head);
     7     __this_cpu_write(tasklet_vec.head, NULL);
     8     __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);
     9     local_irq_enable();
    10 
    11     while (list) {
    12         struct tasklet_struct *t = list;
    13 
    14         list = list->next;
    15 
    16         if (tasklet_trylock(t)) {
    17             if (!atomic_read(&t->count)) {
    18                 if (!test_and_clear_bit(TASKLET_STATE_SCHED,
    19                             &t->state))
    20                     BUG();
    21                 t->func(t->data);
    22                 tasklet_unlock(t);
    23                 continue;
    24             }
    25             tasklet_unlock(t);
    26         }
    27 
    28         local_irq_disable();
    29         t->next = NULL;
    30         *__this_cpu_read(tasklet_vec.tail) = t;
    31         __this_cpu_write(tasklet_vec.tail, &(t->next));
    32         __raise_softirq_irqoff(TASKLET_SOFTIRQ);
    33         local_irq_enable();
    34     }
    35 }

    第6行中tasklet_vec数组中和本地cpu相关的元素的head域值,保存在list变量中,实际上把当前cpu的tastlet任务链表卸下来,list成为链表头指针。第7,8行把tasklet_vec数组和本地cpu相关的元素的head域置为NULL,tail域指向head域。第11-26行用while循环执行所有的tasklet函数。第21行是tasklet函数的执行。第30-31行把执行之后的struct tasklet_struct链表节点重新挂到tasklet_vec数组中,由此可见装tasklet函数的结构体不用程序员手动创建,由系统来提供。第32行重新设置软中断掩码表的相应位。TASKLET_SOFTIRQ所对应的软中断函数是tasklet_hi_action,和tasklet_action执行过程类似,不再分析。

    来看下tasklet_action函数初始化过程(kernel/softirq.c)。

     1 void __init softirq_init(void)
     2 {
     3     int cpu;
     4 
     5     for_each_possible_cpu(cpu) {
     6         per_cpu(tasklet_vec, cpu).tail =
     7             &per_cpu(tasklet_vec, cpu).head;
     8         per_cpu(tasklet_hi_vec, cpu).tail =
     9             &per_cpu(tasklet_hi_vec, cpu).head;
    10     }
    11 
    12     open_softirq(TASKLET_SOFTIRQ, tasklet_action);
    13     open_softirq(HI_SOFTIRQ, tasklet_hi_action);
    14 }

    第5-10行,初始化tasklet_vec和tasklet_hi_vec两个数组的元素,使每个元素的tail域指向head域。cpu编号作为两个数组的下标。第12-13行将负责处理tasklet任务的两个软中断函数tasklet_action和tasklet_hi_action分别存入软中断数组softirq_vec[NR_SOFTIRQS]的相应元素中。来看下open_softirq函数(kernel/softirq.c)。

    1 void open_softirq(int nr, void (*action)(struct softirq_action *))
    2 {
    3     softirq_vec[nr].action = action;
    4 } 

    相信你一看就明白了,不用解释了~

    再看看tasklet的初始化过程(kernel/softirq.c)

    1 void tasklet_init(struct tasklet_struct *t,
    2           void (*func)(unsigned long), unsigned long data)
    3 {
    4     t->next = NULL;
    5     t->state = 0;
    6     atomic_set(&t->count, 0);
    7     t->func = func;
    8     t->data = data;
    9 }

    该函数接收tasklet_struct结构体指针和小任务函数指针,然后对tasklet_struct结构体进行初始化,并将函数指针赋给t->func域。tasklet_init函数在tasklet_hrtimer_init函数中被调用。

     3.tasklet调度(这两个函数分别在include/linux/interrupt.h和kernel/softirq.c文件中)

    1 static inline void tasklet_schedule(struct tasklet_struct *t)
    2 {
    3     if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
    4         __tasklet_schedule(t);
    5 }
     1 void __tasklet_schedule(struct tasklet_struct *t)
     2 {
     3     unsigned long flags;
     4 
     5     local_irq_save(flags);
     6     t->next = NULL;
     7     *__this_cpu_read(tasklet_vec.tail) = t;
     8     __this_cpu_write(tasklet_vec.tail, &(t->next));
     9     raise_softirq_irqoff(TASKLET_SOFTIRQ);
    10     local_irq_restore(flags);
    11 }
    12 EXPORT_SYMBOL(__tasklet_schedule);

    在__tasklet_schedule函数第7-8行,将struct tasklet_struct类型的tasklet挂到taskeletvec数组中,然后第9行进行软中断掩码置位。之后当软中断被执行时,tasklet函数就可以被执行了。

    至此,tasklet的初始化就分析完了。

  • 相关阅读:
    Convert Sorted Array to Binary Search Tree数组变成高度平衡的二叉树
    695. Max Area of Island最大岛屿面积
    257. Binary Tree Paths返回所有深度优先的遍历
    112. Path Sum二叉树路径和
    对称二叉树 · symmetric binary tree
    100. Same Tree同样的树
    计数排序 (Counting Sort)
    基数排序 (Radix Sort)
    Linux 文件基本属性
    gensuitemodule (Mac OS) – Python 中文开发手册
  • 原文地址:https://www.cnblogs.com/liangning/p/3876656.html
Copyright © 2020-2023  润新知