Linux内核中的软中断、tasklet和工作队列详解

更新时间:2023-07-17 14:02:22 阅读: 评论:0

Linux内核中的软中断、tasklet和⼯作队列详解
[TOC]
本⽂基于Linux2.6.32内核版本。
引⾔
软中断、tasklet和⼯作队列并不是Linux内核中⼀直存在的机制,⽽是由更早版本的内核中的“下半部”(bottom half)演变⽽来。下半部的机制实际上包括五种,但2.6版本的内核中,下半部和任务队列的函数都消失了,只剩下了前三者。
介绍这三种下半部实现之前,有必要说⼀下上半部与下半部的区别。
上半部指的是中断处理程序,下半部则指的是⼀些虽然与中断有相关性但是可以延后执⾏的任务。举个例⼦:在⽹络传输中,⽹卡接收到数据包这个事件不⼀定需要马上被处理,适合⽤下半部去实现;但是⽤户敲击键盘这样的事件就必须马上被响应,应该⽤中断实现。
两者的主要区别在于:中断不能被相同类型的中断打断,⽽下半部依然可以被中断打断;中断对于时间⾮常敏感,⽽下半部基本上都是⼀些可以延迟的⼯作。由于⼆者的这种区别,所以对于⼀个⼯作是放在上半部还是放在下半部去执⾏,可以参考下⾯4条:
1. 如果⼀个任务对时间⾮常敏感,将其放在中断处理程序中执⾏。
2. 如果⼀个任务和硬件相关,将其放在中断处理程序中执⾏。
3. 如果⼀个任务要保证不被其他中断(特别是相同的中断)打断,将其放在中断处理程序中执⾏。
4. 其他所有任务,考虑放在下半部去执⾏。
兔子的英文怎么写
孙悟空的坟墓有写内核任务需要延后执⾏,因此才有的下半部,进⽽实现了三种实现下半部的⽅法。这就是本⽂要讨论的软中断、tasklet和⼯作队列。
下表可以更直观的看到它们之间的关系。
软中断
软中断作为下半部机制的代表,是随着SMP(share memory processor)的出现应运⽽⽣的,它也是tasklet实现的基础(tasklet实际上只是在软中断的基础上添加了⼀定的机制)。软中断⼀般是“可延迟函数”的总称,有时候也包括了tasklet(请读者在遇到的时候根据上下⽂推断是否包含tasklet)。它的出现就是因为要满⾜上⾯所提出的上半部和下半部的区别,使得对时间不敏感的任务延后执⾏,⽽且可以在多个CPU上并⾏执⾏,使得总的系统效率可以更⾼。它的特性包括:
产⽣后并不是马上可以执⾏,必须要等待内核的调度才能执⾏。软中断不能被⾃⼰打断(即单个cpu上软中断不能嵌套执⾏),只能被硬件中断打断(上半部)。
可以并发运⾏在多个CPU上(即使同⼀类型的也可以)。所以软中断必须设计为可重⼊的函数(允许多个CPU同时操作),因此也需要使⽤⾃旋锁来保其数据结构。
相关数据结构
软中断描述符
struct softirq_action{ void (*action)(struct softirq_action *);};
描述每⼀种类型的软中断,其中void(*action)是软中断触发时的执⾏函数。
软中断全局数据和类型
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
enum
{
关繁体HI_SOFTIRQ=0, /*⽤于⾼优先级的tasklet*/
TIMER_SOFTIRQ, /*⽤于定时器的下半部*/
NET_TX_SOFTIRQ, /*⽤于⽹络层发包*/
NET_RX_SOFTIRQ, /*⽤于⽹络层收报*/
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ, /*⽤于低优先级的tasklet*/
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
相关API
注册软中断
void open_softirq(int nr, void (*action)(struct softirq_action *))
即注册对应类型的处理函数到全局数组softirq_vec中。例如⽹络发包对应类型为NET_TX_SOFTIRQ的处理函数net_tx_action.
触发软中断
void rai_softir q(unsigned int nr)
实际上即以软中断类型nr作为偏移量置位每cpu变量irq_stat[cpu_id]的成员变量__softirq_pending,这也是同⼀类型软中断可以在多个cpu上并⾏运⾏的根本原因。
软中断执⾏函数
do_softirq-->__do_softirq
执⾏软中断处理函数__do_softirq前⾸先要满⾜两个条件:
(1)不在中断中(硬中断、软中断和NMI) 。
(2)有软中断处于pending状态。
系统这么设计是为了避免软件中断在中断嵌套中被调⽤,并且达到在单个CPU上软件中断不能被重⼊的⽬的。对于ARM架构的CPU不存在中断嵌套中调⽤软件中断的问题,因为ARM架构的CPU在处理硬件中断的过程中是关闭掉中断的。只有在进⼊了软中断处理过程中之后才会开启硬件中断,如果在软件中断处理过程中有硬件中断嵌套,也不会再次调⽤软中断,becau硬件中断是软件中断处理过程中再次进⼊的,此时preempt_count已经记录了软件中断!对于其它架构的CPU,有可能在触发调⽤软件中断前,也就是还在处理硬件中断的时候,就已经开启了硬件中断,可能会发⽣中断嵌套,在中断嵌套中是不允许调⽤软件中断处理的。Why?我的理解是,在发⽣中断嵌套的时候,表明这个时
奇葩比赛候是系统突发繁忙的时候,内核第⼀要务就是赶紧把中断中的事情处理完成,退出中断嵌套。避免多次嵌套,哪⾥有时间处理软件中断,所以把软件中断推迟到了所有中断处理完成的时候才能触发软件中断。
实现原理和实例
软中断的调度时机:
1. do_irq完成I/O中断时调⽤irq_exit。
2. 系统使⽤I/O APIC,在处理完本地时钟中断时。
3. local_bh_enable,即开启本地软中断时。
十大工程
4. SMP系统中,cpu处理完被CALL_FUNCTION_VECTOR处理器间中断所触发的函数时。
5. ksoftirqd/n线程被唤醒时。
下⾯以从中断处理返回函数irq_exit中调⽤软中断为例详细说明。
触发和初始化的的流程如图所⽰:
软中断处理流程
asmlinkage void __do_softir q(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;
击掌英语pending = local_softirq_pending();
account_system_vtime(current);
__local_bh_disable((unsigned long)__builtin_return_address(0));    lockdep_softirq_enter();
cpu = smp_processor_id();
restart:
/* Ret the pending bitmask before enabling irqs */
t_softirq_pending(0);
local_irq_enable();
h = softirq_vec;
do {
乙免if (pending & 1) {
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(h - softirq_vec);
trace_softirq_entry(h, softirq_vec);
h->action(h);
trace_softirq_exit(h, softirq_vec);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %td%s%p"
"with preempt_count %08x,"
" exited with %08x?\n", h - softirq_vec,
softirq_to_name[h - softirq_vec],
h->action, prev_count, preempt_count());
preempt_count() = prev_count;
}
rcu_bh_qs(cpu);
}
h++;
pending >>= 1;
} while (pending);
local_irq_disable();
pending = local_softirq_pending();
if (pending && --max_restart)
goto restart;
if (pending)
有趣的历史小故事
wakeup_softirqd();
lockdep_softirq_exit();
account_system_vtime(current);
_local_bh_enable();
}
1. ⾸先调⽤local_softirq_pending函数取得⽬前有哪些位存在软件中断。
2. 调⽤__local_bh_disable关闭软中断,其实就是设置正在处理软件中断标记,在同⼀个CPU上使得不能重⼊__do_softirq函数。
3. 重新设置软中断标记为0,t_softirq_pending重新设置软中断标记为0,这样在之后重新开启中断之后硬件中断中⼜可以设置软件
中断位。
4. 调⽤local_irq_enable,开启硬件中断。
5. 之后在⼀个循环中,遍历pending标志的每⼀位,如果这⼀位设置就会调⽤软件中断的处理函数。在这个过程中硬件中断是开启的,
随时可以打断软件中断。这样保证硬件中断不会丢失。
6. 之后关闭硬件中断(local_irq_disable),查看是否⼜有软件中断处于pending状态,如果是,并且在本次调⽤__do_softirq函数过程中
没有累计重复进⼊软件中断处理的次数超过max_restart=10次,就可以重新调⽤软件中断处理。如果超过了10次,就调⽤
wakeup_softirqd()唤醒内核的⼀个进程来处理软件中断。设⽴10次的限制,也是为了避免影响系统响应时间。
7. 调⽤_local_bh_enable开启软中断。
软中断内核线程
之前我们分析的触发软件中断的位置其实是中断上下⽂中,⽽在软中断的内核线程中实际已经是进程的上下⽂。
这⾥说的软中断上下⽂指的就是系统为每个CPU建⽴的ksoftirqd进程。
软中断的内核进程中主要有两个⼤循环,外层的循环处理有软件中断就处理,没有软件中断就休眠。内层的循环处理软件中断,每循环⼀次都试探⼀次是否过长时间占据了CPU,需要调度就释放CPU给其它进程。具体的操作在注释中做了解释。

本文发布于:2023-07-17 14:02:22,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1085144.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:中断   软件   处理
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图