内核源码分析之软中断(基于3.16-rc4)
1.和软中断相关的数据结构:
softing_vec数组(kernel/softirq.c)
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
NR_SOFTIRQS值为10,说明内核支持10个软中断函数。
softirq_action结构体(include/linux/interrupt.h)
struct softirq_action
{
void (*action)(struct softirq_action *);
};
action是函数指针变量,指向了某个软中断函数。
irq_cpustat_t结构体(arch/x86/include/asm/hardirq.h)
typedef struct {
unsigned int __softirq_pending;
unsigned int __nmi_count; /* arch dependent */
#ifdef CONFIG_X86_LOCAL_APIC
unsigned int apic_timer_irqs; /* arch dependent */
unsigned int irq_spurious_count;
unsigned int icr_read_retry_count;
#endif
#ifdef CONFIG_HAVE_KVM
unsigned int kvm_posted_intr_ipis;
#endif
unsigned int x86_platform_ipis; /* arch dependent */
unsigned int apic_perf_irqs;
unsigned int apic_irq_work_irqs;
#ifdef CONFIG_SMP
unsigned int irq_resched_count;
unsigned int irq_call_count;
/*
* irq_tlb_count is double-counted in irq_call_count, so it must be
* subtracted from irq_call_count when displaying irq_call_count
*/
unsigned int irq_tlb_count;
#endif
#ifdef CONFIG_X86_THERMAL_VECTOR
unsigned int irq_thermal_count;
#endif
#ifdef CONFIG_X86_MCE_THRESHOLD
unsigned int irq_threshold_count;
#endif
#if IS_ENABLED(CONFIG_HYPERV) || defined(CONFIG_XEN)
unsigned int irq_hv_callback_count;
#endif
} ____cacheline_aligned irq_cpustat_t;
每个cpu都有一个这样的结构体变量,在软中断中,我们要使用的是第2行的成员,32位的软中断掩码。当有一个软中断被挂起(将要被执行)的时候,会设置该掩码中的相应位。
2.软中断的执行过程
首先使用open_softirq()函数注册软中断函数,代码如下(kernel/softirq.c):
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
将软中断函数指针action存入softirq_vec数组的对应元素中。
接着,使用raise_softirq()激活软中断,代码如下(kernel/softirq.c):
void raise_softirq(unsigned int nr)
{
unsigned long flags; local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
第5行关闭本地中断,第7行恢复中断。第6行激活nr所对应的软中断函数。接着分析 raise_softirq_irqoff(),代码如下(kernel/softirq.c):
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr); /*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt())
wakeup_softirqd();
}
第3行__raise_softirq_irqoff函数设置了软中断掩码的相应位,代码如下(kernel/softirq.c)。然后第14行判断软中断是否已经激活或者被禁用,如果没有,那么在15行激活内核线程ksoftirq,去执行软中断。
void (unsigned int nr)
{
trace_softirq_raise(nr);
or_softirq_pending(1UL << nr);
}
具体而言,在第4行的函数中设置掩码位。
3.下面分析下在哪些地方都可以进入软中断。
第一个地方,当然就是上边所提到的,内核线程ksoftirq被激活的时候。下面看看ksoftirq线程(kernel/softirq.c)。
DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
给每个cpu都定义一个指向struct task_struct类型的结构体变量,很显然,该变量存的是ksoftirq线程的进程描述符。(由此也说明,linux的线程和进程是一个东西)
接着,我们要看看ksoftirq线程要执行的函数(kernel/softirq.c)。
static void run_ksoftirqd(unsigned int cpu)
{
local_irq_disable();
if (local_softirq_pending()) {
/*
* We can safely run softirq on inline stack, as we are not deep
* in the task stack here.
*/
__do_softirq();
rcu_note_context_switch(cpu);
local_irq_enable();
cond_resched();
return;
}
local_irq_enable();
}
该函数就是ksoftirq线程的线程体。第9行__do_softirq()中调用所有软中断函数。
我们回过头来再分析下wakeup_softirqd(),看看ksoftirq线程怎样被唤醒(kernel/softirq.c)。
static void wakeup_softirqd(void)
{
/* Interrupts are disabled: no need to stop preemption */
struct task_struct *tsk = __this_cpu_read(ksoftirqd); if (tsk && tsk->state != TASK_RUNNING)
wake_up_process(tsk);
}
第4行把本地cpu的ksoftirq线程的描述符读到tsk变量中,第6行中判断ksoftirq线程如果没有运行的话,第7行唤醒该线程。
第二个地方,中断处理程序do_IRQ完成处理或者调用irq_exit函数时。下面看看irq_exit代码(kernel/softirq.c)。
void irq_exit(void)
{
#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
local_irq_disable();
#else
WARN_ON_ONCE(!irqs_disabled());
#endif account_irq_exit_time(current);
preempt_count_sub(HARDIRQ_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq(); tick_irq_exit();
rcu_irq_exit();
trace_hardirq_exit(); /* must be last! */
}
不用我说了吧,我觉得你一眼就能瞄见了第12行。看下invoke_softirq函数(kernel/softirq.c)。
static inline void invoke_softirq(void)
{
if (!force_irqthreads) {
#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
/*
* We can safely execute softirq on the current stack if
* it is the irq stack, because it should be near empty
* at this stage.
*/
__do_softirq();
#else
/*
* Otherwise, irq_exit() is called on the task stack that can
* be potentially deep already. So call softirq in its own stack
* to prevent from any overrun.
*/
do_softirq_own_stack();
#endif
} else {
wakeup_softirqd();
}
}
第3行force_irqthreads值为0,所以该函数也调用了__do_softirq()来执行软中断。
还有几处地方暂时不分析了,以后有空补上。
4.下面来看下__do_softirq()函数(kernel/softirq.c)。
asmlinkage __visible void __do_softirq(void)
{
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
unsigned long old_flags = current->flags;
int max_restart = MAX_SOFTIRQ_RESTART;
struct softirq_action *h;
bool in_hardirq;
__u32 pending;
int softirq_bit; /*
* Mask out PF_MEMALLOC s current task context is borrowed for the
* softirq. A softirq handled such as network RX might set PF_MEMALLOC
* again if the socket is related to swap
*/
current->flags &= ~PF_MEMALLOC; pending = local_softirq_pending();
account_irq_enter_time(current); __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
in_hardirq = lockdep_softirq_start(); restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(); local_irq_enable(); h = softirq_vec; while ((softirq_bit = ffs(pending))) {
unsigned int vec_nr;
int prev_count; h += softirq_bit - ; vec_nr = h - softirq_vec;
prev_count = preempt_count(); kstat_incr_softirqs_this_cpu(vec_nr); trace_softirq_entry(vec_nr);
h->action(h);
trace_softirq_exit(vec_nr);
if (unlikely(prev_count != preempt_count())) {
pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
vec_nr, softirq_to_name[vec_nr], h->action,
prev_count, preempt_count());
preempt_count_set(prev_count);
}
h++;
pending >>= softirq_bit;
} rcu_bh_qs(smp_processor_id());
local_irq_disable(); pending = local_softirq_pending();
if (pending) {
if (time_before(jiffies, end) && !need_resched() &&
--max_restart)
goto restart; wakeup_softirqd();
} lockdep_softirq_end(in_hardirq);
account_irq_exit_time(current);
__local_bh_enable(SOFTIRQ_OFFSET);
WARN_ON_ONCE(in_interrupt());
tsk_restore_flags(current, old_flags, PF_MEMALLOC);
}
在该函数中循环调用的所有的被激活的软中断函数。第5行MAX_SOFTIRQ_RESTART值为10,表示最多循环10次(不能让其他进程等太久),第32行获取pending表中第一被设置的比特位,第44行开始执行设置过的软中断函数。第53行对pending进行右移运算,然后进入下次循环。直到将本轮所有已设置的软中断函数全部执行完,退出循环。第59行重新获得本地cpu的软中断掩码,第61行如果时间没有超出end而且没有出现更高优先级的进程并且10次寻环未用完,那么跳回restart,重新for循环。否则,第65行唤醒softirqd内核线程。然后退出本函数。
至此,软中断的处理过程就分析完了。
内核源码分析之软中断(基于3.16-rc4)的更多相关文章
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)
http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...
- 鸿蒙内核源码分析(异常接管篇) | 社会很单纯 , 复杂的是人 | 百篇博客分析OpenHarmony源码 | v39.03
百篇博客系列篇.本篇为: v39.xx 鸿蒙内核源码分析(异常接管篇) | 社会很单纯,复杂的是人 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU ...
- 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 百篇博客分析OpenHarmony源码 | v38.02
百篇博客系列篇.本篇为: v38.xx 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...
- 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 百篇博客分析OpenHarmony源码 | v36.04
百篇博客系列篇.本篇为: v36.xx 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CP ...
- Linux内核源码分析方法
一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...
- Linux内核源码分析 day01——内存寻址
前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...
- 【转】Linux内核源码分析方法
一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...
- Linux内核源码分析方法_转
Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...
- 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01
百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...
随机推荐
- java:I/O流
I/O是input/output的缩写,即输入输出端口. 从 文件.键盘.网络 等输入到java程序,再从java程序输出到 文件.显示器.网络等 分类: 1.输入流 和 输出流2.字节流 和 字符流 ...
- [cocoapods]如何卸载cocoapods
今天我们来讲一下cocoapods的删除步骤! 1.移除pod组件,打开终端执行which pod 然后输出了路径,我的是 /usr/local/bin/pod 2. 移除Cocoapods组件,继续 ...
- Android ActionBar的Overlay模式如何不遮盖顶部内容的问题
关于actionbar的overlay模式请参考 如何让android的actionbar浮动且透明 一文.这篇文章讲的是如何在这种模式下让actionbar不遮住顶部的内容. 这 一般是这样的场景, ...
- Android开发之消息机制
转:http://stackvoid.com/introduction-to-Message-Handler-in-Android/ http://blog.dreamtobe.cn/2016/03/ ...
- 如何在Ubuntu上安装最新版本的Node.js
apt-get update apt-get install -y python-software-properties software-properties-common add-apt-repo ...
- hdu 4825 Xor Sum (建树) 2014年百度之星程序设计大赛 - 资格赛 1003
题目 题意:给n个数,m次询问,每次给一个数,求这n个数里与这个数 异或 最大的数. 思路:建一个类似字典数的数,把每一个数用 32位的0或者1 表示,查找从高位向底位找,优先找不同的,如果没有不同的 ...
- ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType)
1 imageView.setScaleType(ImageView.ScaleType.FIT_XY ); 1 这里我们重点理解ImageView的属性android:scaleType,即Imag ...
- PL/SQL database character set(AL32UTF8) and Client character set(ZHS16GBK) are different 2012-04-11 13:01
启动PL/SQL Developer 报字符编码不一致错误 Database character set (AL32UTF8) and Client character set (ZHS16GBK) ...
- ACM - ICPC World Finals 2013 D Factors
原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf 题目翻译: 问题描述 一个最基本的算数法则就是大于1的整数 ...
- LA 4119 (差分数列 多项式) Always an integer
题意: 给出一个形如(P)/D的多项式,其中P是n的整系数多项式,D为整数. 问是否对于所有的正整数n,该多项式的值都是整数. 分析: 可以用数学归纳法证明,若P(n)是k次多项式,则P(n+1) - ...