linux 内核tasket机制

tasklet(tasklet,有的书中翻译为“任务蕾”)是基于软中断实现的。为什么要提供tasklet?因为tasklet相对软中断有以下优势。

(1)软中断的种类是编译时静态定义的,在运行时不能添加或删除;tasklet可以在运行时添加或删除。

(2)同一种软中断的处理函数可以在多个处理器上同时执行,处理函数必须是可以重入的,需要使用锁保护临界区;一个tasklet同一时刻只能在一个处理器上执行,不要求处理函数是可以重入的。

tasklet根据优先级分为两种:低优先级tasklet和高优先级tasklet。tasklet是通过软中断HI_SOFTIRQ和TASKLET_SOFTIRQ实现的,所以它们本身也是软中断,存放在两个单处理器数据结构:tasklet_vec和tasklet_hi_vec会在两个软中断中分别处理。

  数据结构


tasklet的数据结构如下:
include/linux/interrupt.h
struct tasklet_struct
{
    struct tasklet_struct *next;    //链表中的下一个tasklet
    unsigned long state;        //tasklet的状态
    atomic_t count;         //禁止计数器
    void (*func)(unsigned long);//tasklet处理函数
    unsigned long data;     //给tasklet处理函数的参数
};
成员 next 用来把tasklet添加到单向链表中。
成员 state 是tasklet的状态,取值如下。
(1) 0:tasklet没有被调度。
(2) (1 << TASKLET_STATE_SCHED):tasklet被调度,即将被执行。
(3) (1 << TASKLET_STATE_RUN):只在多处理器系统中使用,表示tasklet正在执行。成员 count 是计数, 0 表示允许tasklet被执行,非零值表示禁止tasklet被执行。成员 func 是处理函数,成员 data 是传给处理函数的参数。
每个处理器有两条单向链表:低优先级tasklet链表和高优先级tasklet链表。
kernel/softirq.c
struct tasklet_head {
    struct tasklet_struct *head;
    struct tasklet_struct **tail;
};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);


 编程接口


定义一个静态的tasklet,并且允许tasklet被执行,方法如下:
DECLARE_TASKLET(name, func, data)

定义一个静态的tasklet,并且禁止tasklet被执行,方法如下:
DECLARE_TASKLET_DISABLED(name, func, data)

在运行时动态初始化tasklet,并且允许tasklet被执行,方法如下:
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);

函数 tasklet_disable()用来禁止tasklet被执行,如果tasklet正在被执行,该函数等待tasklet执行完。
void tasklet_disable(struct tasklet_struct *t);

函数 tasklet_disable_nosync()用来禁止tasklet被执行,如果tasklet正在被执行,该函数不会等待tasklet执行完。
void tasklet_disable_nosync(struct tasklet_struct *t);

函数 tasklet_enable()用来允许tasklet被执行。
void tasklet_enable(struct tasklet_struct *t);

函数 tasklet_schedule()用来调度低优先级tasklet: 把tasklet添加到当前处理器的低优先级tasklet链表中,并且触发低优先级tasklet软中断。
void tasklet_schedule(struct tasklet_struct *t);

函数 tasklet_hi_schedule()用来调度高优先级tasklet:把tasklet添加到当前处理器的高优先级tasklet链表的尾部,并且触发高优先级tasklet软中断。
void tasklet_hi_schedule(struct tasklet_struct *t);

函数 tasklet_hi_schedule_first()用来调度高优先级tasklet:把tasklet添加到当前处理器的高优先级tasklet链表的首部,并且触发高优先级tasklet软中断。
void tasklet_hi_schedule_first(struct tasklet_struct *t);

函数 tasklet_kill()用来杀死tasklet,确保tasklet不会被调度和执行。如果tasklet正在被执行,该函数等待tasklet执行完。通常在卸载内核模块的时候调用该函数。
void tasklet_kill(struct tasklet_struct *t);


  技术原理


tasklet是基于软中断实现的,根据优先级分为两种:低优先级tasklet和高优先级tasklet。软中断 HI_SOFTIRQ 执行高优先级tasklet,软中断 TASKLET_SOFTIRQ 执行低优先级tasklet。
(1)调度tasklet。
函数 tasklet_schedule()用来调度低优先级tasklet,函数 tasklet_hi_schedule()用来调度高优先级tasklet。以函数 tasklet_schedule()为例说明,其代码如下:
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);
}
kernel/softirq.c
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没有被调度过,那么首先设置调度标志位,然后把tasklet添加到当前处理器的低优先级tasklet链表的尾部,最后触发软中断 TASKLET_SOFTIRQ。
(2)执行tasklet。
初始化的时候,把软中断 TASKLET_SOFTIRQ 的处理函数注册为函数 tasklet_action,把软中断 HI_SOFTIRQ 的处理函数注册为函数 tasklet_hi_action。
kernel/softirq.c
void __init softirq_init(void)
{
    …
    open_softirq(TASKLET_SOFTIRQ, tasklet_action);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
以函数 tasklet_action()为例说明,其代码如下:
kernel/softirq.c
1 static __latent_entropy 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, this_cpu_ptr(&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~8 行代码,把当前处理器的低优先级tasklet链表中的所有tasklet移到临时链表list 中。
第 11 行代码,遍历临时链表 list,依次处理每个tasklet,如下。
1)第 16 行代码,尝试锁住tasklet,确保一个tasklet同一时刻只在一个处理器上执行。
2)第 17 行代码,如果tasklet的计数为 0,表示允许tasklet被执行。
3)第 18 行代码,清除tasklet的调度标志位,其他处理器可以调度这个tasklet,但是不能执行这个tasklet。
4)第 21 行代码,执行tasklet的处理函数。
5)第 22 行代码,释放tasklet的锁,其他处理器就可以执行这个tasklet了。
6)第 29~32 行代码,如果尝试锁住tasklet失败(表示tasklet正在其他处理器上执行),或者禁止tasklet被执行,那么把tasklet重新添加到当前处理器的低优先级tasklet链表的尾部,然后触发软中断 TASKLET_SOFTIRQ。


  tasklet的简单用法


 下面是tasklet的一个简单应用, 以模块的形成加载。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>

static struct tasklet_struct my_tasklet;
static void tasklet_handler (unsigned long data)
{
    printk(KERN_ALERT "tasklet_handler is running.\n");
}

static int __init test_init(void)
{
    tasklet_init(&my_tasklet, tasklet_handler, 0);
    tasklet_schedule(&my_tasklet);
    return 0;
}

static void __exit test_exit(void)
{
    tasklet_kill(&my_tasklet);
    printk(KERN_ALERT "test_exit running.\n");
}
MODULE_LICENSE("GPL");

module_init(test_init);
module_exit(test_exit);

从这个例子可以看出,所谓的tasklet机制是为下半部函数的执行提供了一种执行机制,也就是说,推迟处理的事情是由tasklet_handler实现,何时执行,经由tasklet机制封装后交给内核去处理。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/178262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谈谈MYSQL中的Explain

目录 前言 基本介绍 Explain结果列解读 概述 key key_len type extra 前言 我们在设计一个系统的时候&#xff0c;有时候通常为了基础业务&#xff0c;写出的查询sql语句并不高效&#xff0c;从而影响到用户使用系统的整体体验感不是很好&#xff0c;我们通常在系统的…

一篇文章搞懂 JavaScript 箭头函数

一篇文章搞懂 JavaScript 箭头函数 JavaScript 箭头函数的引入为我们编写简洁且易读的代码提供了便利。 箭头函数是 ES6&#xff08;ECMAScript 2015&#xff09; 引入的新语法&#xff0c;它可以用一种更简单的方式来定义函数。本文将深入探讨箭头函数的特性、用法和一些最佳…

百度地图定位

https://lbsyun.baidu.com/index.php?titlejspopularGL/guide/geoloaction // 百度地图API功能 var map new BMap.Map(map)var point new BMap.Point(108.95, 34.27)map.centerAndZoom(point, 12)var geolocation new BMap.Geolocation()geolocation.getCurrentPosition(f…

Python常见基础数据结构

Python常见基础数据结构 字符串字符串的构造字符串是一种序列正向索引负向索引有限切片无限切片查询方法压缩方法替换方法格式化插入分割方法 列表列表构造列表同样属于序列列表的元素增加列表其他方法 元组字典字典的构造字典不属于序列字典可变 字符串 字符串的构造 • 单引…

Python神器解密:掌握property特性巧管理属性

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com property 是Python中的一个内置装饰器&#xff0c;它用于创建属性并允许开发者定义特定的操作&#xff0c;例如获取&#xff08;getter&#xff09;、设置&#xff08;setter&#xff09;和删除&#xff08;dele…

【强化学习】关于PPO、SAC算法中价值函数的一些胡思乱想

最近的一些关于强化学习中价值函数的一些想法&#xff0c;发在了知乎上&#xff0c;全文考过来调公式有点麻烦&#xff0c;就直接发链接算了。 https://zhuanlan.zhihu.com/p/600300791?utm_psn1712895542018043904 https://zhuanlan.zhihu.com/p/667868767?utm_psn17128948…

Vue中的过滤器了解吗?过滤器的应用场景有哪些?

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue-filter过滤器 目录 一、是什么 二、如何用 定义filter 小结&#xff1a; 三、应用场景 四…

苍穹外卖项目笔记(6)— Redis操作营业状态设置

1 在 Java 中操作 Redis 1.1 Redis 的 Java 客户端 Jedis&#xff08;官方推荐&#xff0c;且命令语句同 redis 命令&#xff09;Lettuce&#xff08;底层基于 Netty 多线程框架实现&#xff0c;性能高效&#xff09;Spring Data Redis&#xff08;对 Jedis 和 Lettuce 进行了…

c++ 共享内存

共享内存 一、查看所有的共享内存 (py37) hqnuc:~/tmp/hq$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 4 blueberry 600 524288 2 dest 0x000000…

力扣labuladong——一刷day54

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣1660. 纠正二叉树二、力扣100. 相同的树三、力扣572. 另一棵树的子树四、力扣101. 对称二叉树 前言 判断树是否相等&#xff0c;就直接遍历就可以。判断…

同为科技(TOWE)模块化定制化让每条PDU实现专属供电解决方案

作为追求最高功率和空间效率的动态数据中心的理想产品&#xff0c;模块化、定制化PDU是追求最高功率和空间效率的动态数据中心的理想产品。同为科技&#xff08;TOWE&#xff09;是我国PDU行业的开创者和领导者&#xff0c;曾率先于中国电源分配单元http://www.pdu.com.cn网站上…

机器学习实战第3天:手写数字识别

☁️主页 Nowl &#x1f525;专栏《机器学习实战》 《机器学习》 &#x1f4d1;君子坐而论道&#xff0c;少年起而行之 ​ 文章目录 一、任务描述 二、数据集描述 三、主要代码 &#xff08;1&#xff09;主要代码库的说明与导入方法 &#xff08;2&#xff09;数据预…

2023-11-28 C语言if语句多个||或 与的时时候,会顺序执行,不满足条件则退出,不会再比较后面的内容,实例测试

一、if(i > 0 || image(0) > 5)或者if(i > 0 && image(0) > 5)&#xff0c;C语言if语句多个||或 与&&的时时候&#xff0c;会顺序执行&#xff0c;不满足条件则退出&#xff0c;不会再比较后面的内容。 二、测试代码 #include <stdio.h> #i…

题目标题:汽水瓶 c语言

1.问题描述 有这样一道智力题&#xff1a;“某商店规定&#xff1a;三个空汽水瓶可以换一瓶汽水。小张手上有十个空汽水瓶&#xff0c;她最多可以换多少瓶汽水喝&#xff1f;”答案是5瓶&#xff0c;方法如下&#xff1a;先用9个空瓶子换3瓶汽水&#xff0c;喝掉3瓶满的&#x…

成为网络安全高手!教你如何做出专业级别的渗透测试

01、信息收集 1、域名、IP、端口 域名信息查询&#xff1a;信息可用于后续渗透 IP信息查询&#xff1a;确认域名对应IP&#xff0c;确认IP是否真实&#xff0c;确认通信是否正常 端口信息查询&#xff1a;NMap扫描&#xff0c;确认开放端口 发现&#xff1a;一共开放两…

Linux CentOS_7解决无法上网的问题

参考视频&#xff1a;保姆式教学虚拟机联网liunx(centos)_哔哩哔哩_bilibili 第一步&#xff1a;选择网络模式 第二步&#xff1a;配置网卡命令&#xff1a;打开终端执行命令&#xff1a; 1、先切换到根目录下&#xff0c;防止在第执行cd /etc/sysconfig/network-scripts命的…

java多线程-扩展知识一:进程线程、并发并行、同步异步

1、进程 进程&#xff08;Process&#xff09;是计算机中的程序关于某数据集合上的一次运行活动&#xff0c;是系统进行资源分配的基本单位&#xff0c;是操作系统结构的基础。在早期面向进程设计的计算机结构中&#xff0c;进程是程序的基本执行实体&#xff1b;在当代面向线程…

云计算——ACA学习 阿里云云计算服务概述

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 目录 写在前面 前期回顾 本期介绍 前言了解 一…

QCheckBox通过qss实现状态切换显示不同图标

QString qss "QCheckBox::indicator:unchecked{ \image:url(:/resources/status_close.png); \} \QCheckBox::indicator:checked { \image: url(:/resources/status_open.png); \}";ui->checkBox->setStyleSheet(qss);ui->checkBox->setChecked(true);