2019独角兽企业重金招聘Python工程师标准>>>

内核 tasklet 机制是在软中断的基础上实现的。我们知道软中断有如下两点,导致比较难用:

(1)软中断在内核中静态注册。如果要增加新的软中断,必须修改内核代码,重新编译内核镜像。

(2)SMP系统中,软中断action被所有CPU共享(即,同一个软中断同时可以在多个CPU上运行),需要用户自己保证软中断处理函数的可重入性。

tasklet 设计目标就是解决软中断如上两点不足之处,使得中断处理简单易用。针对第一点,tasklet设计了链表结构,提供 tasklet_schedule() 和 tasklet_hi_schedule() 等接口供用户动态注册 tasklet 处理函数;针对第二点,tasklet 设计了per-cpu变量(即,每个CPU都维护自己的一份 tasklet链表结构),从而避免了同一个 tasklet 被多个CPU共享,使得 tasklet 的处理函数不必是可重入的。

tasklet 占用两种软中断类型,一种是 HI_SOFTIRQ,代表高优先级的tasklet;另外一种是 TASKLET_SOFTIRQ,代表低优先级的 tasklet,两种类型的 tasklet 在实现原理上一样,以下仅以 TASKLET_SOFTIRQ 为例分析其工作原理。

tasklet 数据关系如下图所示:

1,tasklet及链表结构:

struct tasklet_struct
{
    struct tasklet_struct *next;    // 链表指针
    unsigned long state;    // 状态:是否运行,是否被调度
    atomic_t count;    // 引用计数
    void (*func)(unsigned long);    // tasklet处理函数
    unsigned long data;    // tasklet处理数据
};

struct tasklet_head {
    struct tasklet_struct *head;    // 指向tasklet链表头
    struct tasklet_struct **tail;    // 指向tasklet链表尾
};

    2,tasklet软中断注册:

由于tasklet是基于软中断实现的,那么tasklet首先得在软中断上注册一个软中断处理函数 tasklet_action(),然后,被软中断调度运行的 tasklet_action 在per-cpu变量 tasklet_vec中找到当前CPU对应的 tasklet_head 链表,一次处理完该链表上所有的 tasklet_struct:

void __init softirq_init(void)
{
    int cpu;

for_each_possible_cpu(cpu) {
        per_cpu(tasklet_vec, cpu).tail =
            &per_cpu(tasklet_vec, cpu).head;
        per_cpu(tasklet_hi_vec, cpu).tail =
            &per_cpu(tasklet_hi_vec, cpu).head;
    }

open_softirq(TASKLET_SOFTIRQ, tasklet_action);    // 注册低优先级 tasklet 处理函数 tasklet_action
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);    // 注册高优先级 tasklet 处理函数 tasklet_hi_action
}

3,调度运行每个 tasklet_struct:

static void tasklet_action(struct softirq_action *a)
{
    struct tasklet_struct *list;

local_irq_disable();     // 关中断操作链表
    list = __this_cpu_read(tasklet_vec.head);    // 从 tasklet_vec 中一次性获取当前CPU上的所有 tasklet_struct
    __this_cpu_write(tasklet_vec.head, NULL);
    __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));
    local_irq_enable();    // 开中断

while (list) {    // 循环处理链表中所有 tasklet_struct
        struct tasklet_struct *t = list;

list = list->next;

if (tasklet_trylock(t)) {
            if (!atomic_read(&t->count)) {
                if (!test_and_clear_bit(TASKLET_STATE_SCHED,
                            &t->state))
                    BUG();
                t->func(t->data);    // 调度 tasklet 处理函数
                tasklet_unlock(t);
                continue;
            }
            tasklet_unlock(t);
        }

local_irq_disable();
        t->next = NULL;
        *__this_cpu_read(tasklet_vec.tail) = t;
        __this_cpu_write(tasklet_vec.tail, &(t->next));
        __raise_softirq_irqoff(TASKLET_SOFTIRQ);
        local_irq_enable();
    }
}
    4,tasklet的用户初始化和动态注册:

(1)用户初始化 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;
}

(2)用户注册 tasklet:

static inline void tasklet_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_schedule(t);
}

void __tasklet_schedule(struct tasklet_struct *t)
{
    unsigned long flags;

local_irq_save(flags);
    t->next = NULL;
    *__this_cpu_read(tasklet_vec.tail) = t;
    __this_cpu_write(tasklet_vec.tail, &(t->next));
    raise_softirq_irqoff(TASKLET_SOFTIRQ);
    local_irq_restore(flags);
}
    tasklet_schedule() 将用户定义的 tasklet_struct 加入当前CPU的 tasklet 链表中,等待 tasklet_action()在适当的时候调度执行。

    注意:由于 tasklet_action() 处理方式是:一次性退出所有当前CPU上的 tasklet_struct,所以调用一次 tasklet_schedule(),该tasklet_struct只会被调度运行一次,如果需要重复执行,需要用户自己重复调用 tasklet_schedule。

转载于:https://my.oschina.net/yepanl/blog/3050052

Kernel tasklet相关推荐

  1. Linux kernel的中断子系统之(九):tasklet

    返回目录:<ARM-Linux中断系统>. 总结: 二介绍了tasklet存在的意义. 三介绍了通过tasklet_struct来抽想一个tasklet,每个CPU维护一个tasklet链 ...

  2. linux中的tasklet机制【转】

    转自:http://blog.csdn.net/yasin_lee/article/details/12999099 转自: http://www.kerneltravel.net/?p=143 中断 ...

  3. kernel笔记——中断

    cpu与磁盘.网卡.键盘等外围设备(相对于cpu和内存而言)交互时,cpu下发I/O请求到这些设备后,相对cpu的处理能力而言,磁盘.网卡等设备需要较长时间完成请求处理. 那么在请求发出到处理完成这段 ...

  4. 【linux kernel】 中断处理-中断下半部【转】

    转自:http://www.cnblogs.com/embedded-tzp/p/4453987.html 欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地 ...

  5. 【kernel 中内存分配那点事】

    首先呢作为车载bsp开发人员,写大量的内核代码是不现实的事情,多数都是修修改改,但是要有内核代码阅读浏览理解的能力,毕竟linux kernel 还是很nb 的,所有技术人员深入研究内核代码是必须的, ...

  6. vrml场景实例代码_高并发的中断下半部tasklet实例解析

    本文转自AliDataOps 最近为了解决一个技术问题,需要用到内核里中断下半部的tasklet机制,使用过程遇到了非常有趣的问题.在解决问题过程中,也逐步加深了对tasklet机制的理解.本文把这些 ...

  7. 【Linux开发】linux设备驱动归纳总结(六):3.中断的上半部和下半部——tasklet...

    linux设备驱动归纳总结(六):3.中断的上半部和下半部--tasklet xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  8. softirq/tasklet/workqueue的区别

    2011年威盛Linux software的笔试题,我想了半天,就想出了工作队列允许睡眠...真后悔驱动没能深入看看 softirq和tasklet都属于软中断,tasklet是softirq的特殊实 ...

  9. Linux中断 - tasklet

    一.前言 对于中断处理而言,linux将其分成了两个部分,一个叫做中断handler(top half),属于不那么紧急需要处理的事情被推迟执行,我们称之deferable task,或者叫做bott ...

最新文章

  1. 2018 ACM博士论文奖公布:伯克利博士获奖,清华姚班马腾宇荣誉提名(附论文链接)...
  2. IDE接口驱动程序移植
  3. python turtle怎么用变量_Python Turtle绘图指定变量时出现问题
  4. 2009年美国大学计算机专业排名
  5. 《设计团队协作权威指南》—第1章1.3节甘为螺丝钉
  6. git使用—rebase还是merge
  7. c6011取消对null指针的引用_C/C++学习笔记——C提高:指针强化
  8. __declspec(naked)和__asm编写实践总结
  9. ant design的自定义主题 modifyVars无效的原因
  10. mysql 主备XtraBackup恢复
  11. UITextField对字符串的个数限制
  12. 培训ui设计要学编程吗?
  13. Google 已将“xxxx”标记为恶意扩展程序并阻止安装,解决方案
  14. 将一个javaBean中非空的属性合并到另一个javaBean中
  15. DIV向上滚动(类似新闻)
  16. 利用React 和ant-design 搭建cnode项目的随笔
  17. linux cat命令追加,linux cat命令
  18. 网络质量监控 - 守好入口第一关
  19. pigeon-remoting模块功能分析
  20. Java语言十五讲(总结)

热门文章

  1. Java synchronized 和 volatile 的区别
  2. 什么是API,SDK?它们之间有什么关系?
  3. NIO框架之MINA详解
  4. 一起智慧课堂_智慧课堂与传统课堂相比,优点在哪些
  5. (000) java后台开发之指导思想
  6. (016)java后台开发之Eclipse安装反编译插件
  7. swift_005(Swift的Dictionary 字典)
  8. bash之预定义变量
  9. Softmax 回归 vs. k 个二元分类器
  10. Git_Eclipse:[3]Git初始化工程