关于对 softirq、work_queue、tasklet 学习后的一点总结
本文基于linux版本:4.14.111
简单的总结下 softirq、work_queue、tasklet 三种中断下半部的工作原理及区别,并附上三种形式的简单实例。
一、运行原理
① softirq:
void __do_softirq(void)
{
int max_restart = MAX_SOFTIRQ_RESTART; ///< 10
struct softirq_action *h;
...
pending = local_softirq_pending(); ///< 获取到当前的 pending 结果,也就是各个 softirq 的位或结果
__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET); ///< 标识进入了 softirq,并禁止 preempt
while ((softirq_bit = ffs(pending))) {
...
/* 取出相应的 action 并执行 */
h->action(h);
....
} if (pending) {
/* 如果 softirq 整体运行完一遍后仍有 softirq 请求,那么将再次 restart 运行,最多运行 10 遍 */
if (time_before(jiffies, end) && !need_resched() &&
--max_restart)
goto restart; /* 超过了 10 遍之后,不再 restart 运行,将请求交给处理 softirq 的内核线程,之后开启调度不占用过多时间片 */
wakeup_softirqd();
}
...
}
关于处理 softirq 请求的内核线程:
static void wakeup_softirqd(void)
{
struct task_struct *tsk = __this_cpu_read(ksoftirqd); if (tsk && tsk->state != TASK_RUNNING)
wake_up_process(tsk); ///< 唤醒 ksoftirqd
} static void run_ksoftirqd(unsigned int cpu)
{
local_irq_disable();
if (local_softirq_pending()) {
__do_softirq(); ///< 执行 __do_softirq
local_irq_enable();
cond_resched_rcu_qs();
return;
}
local_irq_enable();
}
可见,ksoftirqd 的处理方式也同样是通过调用 __do_softirq 来运行 softirq。
softirq 的触发方式:
1) 通过 __do_softirq 主动触发,通常在硬中断退出时,即 irq_exit,也会通过调用 invoke_softirq() -> __do_softirq 来触发软中断来执行下半部的 ISR(Interrupt Service Routines);
2) 通过 ksoftirqd 被动触发;
② work_queue:依赖的就是内核线程,会在之后的文章中详细说明一下,并会在此处附上链接(以 SPI Flash 驱动中的 kthread 相关操作为例)。
③ tasklet:是 softirq 中的一个 action,可理解为是一个特殊的 softirq,并在 softirq_init 时就得到初始化,其回调函数为 tasklet_action,这里不在阐述,同样运行在中断上下文。
二、工作方式的区别
softirq 与 tasklet 运行在中断上下文,运行期间不可出现 sleep 休眠、阻塞等操作,work_queue 运行在进程上下文,可以进行休眠、阻塞、发生调度等。
三、测试实例及运行结果
① softirq:
1) 第一部分是对内核的修改,因为 softirq 只能静态创建,修改文件为 /kernel/softirq.c 补丁文件如下:
const char * const softirq_to_name[NR_SOFTIRQS] = {
"HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "IRQ_POLL",
- "TASKLET", "SCHED", "HRTIMER", "RCU"
+ "TASKLET", "SCHED", "HRTIMER", "RCU", "LANCE_TEST"
}; /*
@@ -441,6 +441,7 @@ void raise_softirq(unsigned int nr)
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
+EXPORT_SYMBOL(raise_softirq); +static __latent_entropy void softirq_test_action(struct softirq_action *a)
+{
+ printk("This is softirq test.\n");
+}
+
void __init softirq_init(void)
{
int cpu;
@@ -652,6 +658,8 @@ void __init softirq_init(void) open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
+
+ open_softirq(LANCE_TEST, softirq_test_action);
}
2) 编写测试模块:
#include <linux/export.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/notifier.h>
#include <linux/percpu.h>
#include <linux/cpu.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/rcupdate.h>
#include <linux/ftrace.h>
#include <linux/smp.h>
#include <linux/smpboot.h>
#include <linux/tick.h>
#include <linux/irq.h> #include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h> static int softirq_test_init(void)
{
raise_softirq(LANCE_TEST);
msleep();
raise_softirq(LANCE_TEST); return ;
} static void softirq_test_exit(void)
{
} module_init(softirq_test_init);
module_exit(softirq_test_exit);
MODULE_LICENSE("GPL");
3) 安装模块后测试结果:
cat proc/softirqs
CPU0 CPU1 CPU2 CPU3
LANCE_TEST: / # insmod softirq.ko
[ 9544.236966] This is softirq test.
[ 9545.252842] This is softirq test.
② work_queue:
1) 编写测试模块:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/delay.h> static struct work_struct lance_work = {};
static struct workqueue_struct *queue; void work_queue_cb(void)
{
printk(KERN_EMERG "This is work_queue test.\n");
printk("Cur in interrupt context? %s.\n", (in_interrupt() ? "Yes" : "No"));
} static int work_queue_init(void)
{
queue = create_workqueue("work_queue_lance");
INIT_WORK(&lance_work, (typeof(lance_work.func))work_queue_cb);
queue_work(queue, &lance_work); msleep();
queue_work(queue, &lance_work); return ;
} static void work_queue_exit(void)
{
destroy_workqueue(queue);
} module_init(work_queue_init);
module_exit(work_queue_exit);
MODULE_LICENSE("GPL");
2) 安装模块后测试结果:
/ # insmod work_queue.ko
[12633.889983] This is work_queue test.
[12633.893577] Cur in interrupt context? No.
[12634.917039] This is work_queue test.
/ # [12634.920623] Cur in interrupt context? No.
③ tasklet:
1) 编写测试模块:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/interrupt.h> static struct tasklet_struct tasklet = {};
static struct timer_list timer = {}; static void task_func(unsigned long data)
{
printk("This is tasklet test.\n");
printk("Cur in interrupt context? %s.\n", (in_interrupt() ? "Yes" : "No"));
//msleep(1000); ///< 会导致崩溃
} void timer_handler(unsigned long data)
{
tasklet_schedule(&tasklet);
mod_timer(&timer, jiffies+msecs_to_jiffies());
} static int tasklet_test_init(void)
{
tasklet_init(&tasklet, task_func, );
init_timer(&timer); timer.function = timer_handler;
timer.expires = jiffies + HZ;
add_timer(&timer); return ;
} static void tasklet_test_exit(void)
{
del_timer(&timer);
tasklet_disable(&tasklet);
} module_init(tasklet_test_init);
module_exit(tasklet_test_exit);
MODULE_LICENSE("GPL");
2) 安装模块后测试结果:
/ # insmod tasklet.ko
/ # [12477.508765] This is tasklet test.
[12477.512089] Cur in interrupt context? Yes.
[12479.524783] This is tasklet test.
[12479.528108] Cur in interrupt context? Yes.
四、文末总结下在编写一个驱动时, 如果选用这三种工作方式
从根本上来说,如果有休眠阻塞的需要,work_queue 是唯一的选择;
否则最好用 tasklet,如果必须专注于性能的提高,那么就要考虑 softirq,但使用难度较大一些,要注意程序的可重入性。
关于对 softirq、work_queue、tasklet 学习后的一点总结的更多相关文章
- 【原创】Linux中断子系统(三)-softirq和tasklet
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- phpunit测试学习 1:一点简单的扼要有用的东西的总结 一点入门认识
16:45 2015/12/8phpunit测试学习 1:一点简单的扼要有用的东西的总结 一点入门认识 具体的入门安装和入门实践请参照文中的推荐博客或网上其他博客推荐博客,我感觉这几篇博客写得很不错 ...
- 【转载】从创业者角度看《印度合伙人 Padman》后的一点感受
***************************** 这部电影看简介是真实事件改编的,当时除了电影本身的精彩和主角宠妻狂魔之外,印象最深的就是感觉到主角的创业者心态是一步步在生活中被培养的.特别 ...
- 学习javascript 的一点感想
原文:学习javascript 的一点感想 //动态性是指,在一个Javascript对象中,要为一个属性赋值,我们不必事先创建一个字段,只需要在使用的时候做赋值操作即可,如下例:var obj=ne ...
- 2020Java程序员架构师面试宝典,学习后面试必过,震惊,本人通过这篇教程,拿到了0个offer
1. 引言 Java后端学习路线 <吐血整理>顶级程序员工具集 https://github.com/AobingJava/JavaFamily 跟上Java8 经历阿里.头条.腾讯等知名 ...
- SpringCloud学习后获取的地址
关于SpringCloud + Docker 学习地址: (1) https://yq.aliyun.com/articles/57265 (2) https://yq.aliyun.com/team ...
- AE-分享<学习后,制作的视频实例>小视频-与大家交流!
- 从创业者角度看《印度合伙人 Padman》后的一点感受
最近对印度电影颇有兴趣,周末在家看了<印度合伙人 Padman>.本文试着从一名创业者视角,谈谈个人的一点看法. 0.故事简介 引用自 https://movie.douban.com/s ...
- 学习Git的一点心得以及如何把本地修改、删除的代码上传到github中
一:学习Github的资料如下:https://git.oschina.net/progit/ 这是一个学习Git的中文网站,如果诸位能够静下心来阅读,不要求阅读太多,只需要阅读前三章,就可以掌握Gi ...
随机推荐
- SpringCloud与微服务Ⅶ --- Feign负载均衡
官方文档:https://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign 一.Feign是什么 Feign是一 ...
- learn more ,study less(二):整体性学习技术(下)
随意信息的处理 随意信息,或者内容太多.太复杂的信息,都不容易理解,它们需要不同的技术.假 如你发现联想法不能帮助你理解材料,或者需要花费的时间太长,这时候处理随意信息的方 法就很适合了. 这些处理随 ...
- OGG主从表结构不同步,出现OGG-01296错误
一.Cause ogg的err日志出现以下报错 2019-09-10 16:36:55 WARNING OGG-01003 Oracle GoldenGate Delivery for Oracle, ...
- Dubbo(三):深入理解Dubbo源码之如何实现服务引用
一.前言 前面讲了服务是如何导出到注册中心的.其实Dubbo做的一件事就是将服务的URL发布到注册中心上.那现在我们聊一聊消费者一方如何从注册中心订阅服务并进行远程调用的. 二.引用服务时序图 首先总 ...
- Codeforces_842
A.枚举一个区间,判断是否有数符合. #include<bits/stdc++.h> using namespace std; long long l,r,x,y,k; int main( ...
- POJ_2185_二维KMP
http://poj.org/problem?id=2185 求最小覆盖矩阵,把KMP扩展到二维,行一次,列一次,取最小覆盖线段相乘即可. #include<iostream> #incl ...
- Codeforces 1249F Maximum Weight Subset (贪心)
题意 在一颗有点权的树上,选若干个点,使得这些点两两距离大于k,且点权和最大 思路 贪心的取比较大的值即可 将所有点按照深度从大到小排序,如果当前点点权\(a[i]\)大于0,则将距离为k以内的所有点 ...
- Codeforces Gym101234G Dreamoon and NightMarket(优先队列,子集和第k大)
题意: 求子集和第k大,n,k<=1e6 思路: 优先队列经典题目,注意优先队列是默认按从大到小排的 代码: #include<iostream> #include<cstdi ...
- 数据结构与算法的实现(c++)之第一天
开发工具:codeblocks 17.12版本 学习视频来自b站 第一天:学习swap交换.冒泡排序 swap交换:swap是几乎所有的排序的最基础部分,代码如下: #include <iost ...
- 安装Debian的磨磨唧唧
安装Debian的磨磨唧唧 唆使VMware罢工的微软 这两天心血来潮想折腾,拆了我亲爱的Ubuntu.装个Debian 不过因为加入了微软的insider版本 就很尴尬的发现当我打开我的VMware ...