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)的更多相关文章

  1. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  2. 鸿蒙内核源码分析(异常接管篇) | 社会很单纯 , 复杂的是人 | 百篇博客分析OpenHarmony源码 | v39.03

    百篇博客系列篇.本篇为: v39.xx 鸿蒙内核源码分析(异常接管篇) | 社会很单纯,复杂的是人 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU ...

  3. 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 百篇博客分析OpenHarmony源码 | v38.02

    百篇博客系列篇.本篇为: v38.xx 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...

  4. 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 百篇博客分析OpenHarmony源码 | v36.04

    百篇博客系列篇.本篇为: v36.xx 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CP ...

  5. Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

  6. Linux内核源码分析 day01——内存寻址

    前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...

  7. 【转】Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

  8. Linux内核源码分析方法_转

    Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...

  9. 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01

    百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...

随机推荐

  1. JavaScript DOM编程基础精华01(DOM入门,DOM模型和获取页面元素,事件,window对象的方法)

    DOM入门 DOM就是Html页面的模型,将每个标签都做为一个对象,JavaScript通过调用DOM中的属性.方法就可以对网页中的文本框.层等元素进行编程控制.比如通过操作文本框的DOM对象,就可以 ...

  2. nodejs搭配phantomjs highcharts后台生成图表

    简单分享一下,后台使用nodejs结合highcharts.phantomjs生成报表图片的方法.这主要应用在日报邮件. 主要参考以下资料: http://www.highcharts.com/com ...

  3. 《数据通信与网络》笔记--TCP中的拥塞控制

    1.拥塞窗口 发送方窗口的大小不仅取决于接收方,而.而且还取决于网络拥塞的情况. 发送方有2种信息:接收方通告的窗口大小和拥塞窗口的大小,实际的窗口大小事这两者中的最小者. 实际窗口大小 = min( ...

  4. MyBatis学习总结4--解决字段名与实体类属性名不相同的冲突

    在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定是完全相同的,如果直接在xml映射文件中使用sql进行映射,会造成返回值为空的情况,下面阐述解决方案: 测试所用表和数据 create t ...

  5. poshytip两个实用示例

    <html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> < ...

  6. UVa 11181 (条件概率) Probability|Given

    题意: 有n个人买东西,第i个人买东西的概率为Pi.已知最终有r个人买了东西,求每个人买东西的概率. 分析: 设事件E为r个人买了东西,事件Ei为第i个人买了东西.所求为P(Ei|E) = P(EiE ...

  7. BZOJ_1025_[SHOI2009]_游戏_(素数表+最小公倍数+DP)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1025 分析 对于\(n\),转一圈回来之后其实是好几个环各转了整数圈.这些环中的数为\(1,2 ...

  8. main cannot be resolved or is not a field

    今天在做XML解析的时候,总是给我报 XML Parsing Error: XML or text declaration not at start of entity 的错误,后来查了下讲大概意思是 ...

  9. UVALive 3211 Now or later(2-sat)

    2-sat问题,一种在两种可能性中选择必然关系的问题. 推荐两篇论文,也是学2-sat公认比较好的材料.前者较好理解,后者需耐心看. http://www.google.com.hk/url?sa=t ...

  10. error while loading shared libraries: libevent-2.0.so.5解决办法

    安装memcache时,需要建立文件索引或者说文件连接(link),类似windows下的快捷方式 启动服务时出现 error while loading shared libraries: libe ...