`
xitong
  • 浏览: 6173452 次
文章分类
社区版块
存档分类
最新评论

softirq原理以及源码分析

 
阅读更多

Linux 的softirq机制是与SMP紧密不可分的。为此,整个softirq机制的设计与实现中自始自终都贯彻了一个思想:“谁触发,谁执行”(Who marks,Who runs),也即触发软中断的那个CPU负责执行它所触发的软中断,而且每个CPU都由它自己的软中断触发与控制机制。这个设计思想也使得softirq 机制充分利用了SMP系统的性能和特点。多个softirq可以并行执行,甚至同一个softirq可以在多个processor上同时执行。

一、softirq的实现
每个softirq在内核中通过struct softirq_action来表示,另外,通过全局属组softirq_vec标识当前内核支持的所有的softirq。
  1. /* softirq mask and active fields moved to irq_cpustat_t in
  2. * asm/hardirq.h to get better cache usage. KAO
  3. */
  1. struct softirq_action
  2. {
  3. void(*action)(struct softirq_action *);
  4. };
  5. static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
Linux内核最多可以支持32个softirq(思考:为什么是32个?),但当前只实现了10个,如下:
  1. enum
  2. {
  3. HI_SOFTIRQ=0,
  4. TIMER_SOFTIRQ,
  5. NET_TX_SOFTIRQ,
  6. NET_RX_SOFTIRQ,
  7. BLOCK_SOFTIRQ,
  8. BLOCK_IOPOLL_SOFTIRQ,
  9. TASKLET_SOFTIRQ,
  10. SCHED_SOFTIRQ,
  11. HRTIMER_SOFTIRQ,
  12. RCU_SOFTIRQ,/* Preferable RCU should always be the last softirq */
  1. NR_SOFTIRQS
  2. };
二、softirq处理函数
struct softirq_action结构体中,只有一个函数指针成员action,即指向用户定义的softirq处理函数。当执行时,可以通过如下代码:
softirq_vec[i]->action(i);
一个注册的softirq在执行之前必须被激活,术语称为"raise the softirq"。被激活的softirq通常并不会立即执行,一般会在之后的某个时刻检查当前系统中是否有被pending的softirq,如果有就去执行,Linux内核中检查是否有softirq挂起的检查点主要有以下三类:
(1)硬件中断代码返回的时候
  1. /*
  2. * Exit an interrupt context. Process softirqs if needed and possible:
  3. */
  4. void irq_exit(void)
  5. {
  6. account_system_vtime(current);
  7. trace_hardirq_exit();
  8. sub_preempt_count(IRQ_EXIT_OFFSET);
  9. if (!in_interrupt() && local_softirq_pending())
  10. invoke_softirq();
  1. rcu_irq_exit();
  2. #ifdef CONFIG_NO_HZ
  3. /* Make sure that timer wheel updates are propagated */
  4. if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
  5. tick_nohz_stop_sched_tick(0);
  6. #endif
  7. preempt_enable_no_resched();
  8. }
(2)ksoftirqd内核服务线程运行的时候
  1. static int run_ksoftirqd(void * __bind_cpu)
  2. {
  3. ... ...
  4. while (local_softirq_pending()) {
  5. /* Preempt disable stops cpu going offline.
  6. If already offline, we'll be on wrong CPU:
  7. don't process */
  8. if (cpu_is_offline((long)__bind_cpu))
  9. goto wait_to_die;
  10. do_softirq();
  11. preempt_enable_no_resched();
  12. cond_resched();
  13. preempt_disable();
  14. rcu_note_context_switch((long)__bind_cpu);
  15. }
  16. preempt_enable();
  17. set_current_state(TASK_INTERRUPTIBLE);
  18. }
  19. __set_current_state(TASK_RUNNING);
  20. return 0;
  21. ... ...
  22. }
(3)在一些内核子系统中显示的去检查挂起的softirq
  1. int netif_rx_ni(struct sk_buff *skb)
  2. {
  3. int err;
  1. preempt_disable();
  2. err = netif_rx(skb);
  3. if (local_softirq_pending())
  4. do_softirq();
  5. preemptenable();
  6. return err;
  7. }
下面重点分析以下do_softirq(),了解Linux内核到底是怎么来处理softirq的。
  1. asmlinkage void do_softirq(void)
  2. {
  3. unsigned long flags;
  4. struct thread_info *curctx;
  5. union irq_ctx *irqctx;
  6. u32 *isp;
  1. if (in_interrupt()) /*首先判断是否在中断上下文中*/
  2. return;
  3. local_irq_save(flags);
  4. if (local_softirq_pending()) {
  5. curctx = current_thread_info();
  6. irqctx = __get_cpu_var(softirq_ctx);
  7. irqctx->tinfo.task = curctx->task;
  8. irqctx->tinfo.previous_esp = current_stack_pointer;
  9. /* build the stack frame on the softirq stack */
  10. isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
  11. call_on_stack(__do_softirq, isp);
  12. /*
  13. * Shouldnt happen, we returned above if in_interrupt():
  14. */
  15. WARN_ON_ONCE(softirq_count());
  16. }
  17. local_irq_restore(flags);
  18. }
实际的处理函数为__do_softirq:
  1. asmlinkage void __do_softirq(void)
  2. {
  3. struct softirq_action *h;
  4. __u32 pending;
  5. int max_restart = MAX_SOFTIRQ_RESTART; /*不启动ksoftirqd之前,最大的处理softirq的次数,经验值*/
  6. int cpu;
  7. /*取得当前被挂起的softirq,同时这里也解释了为什么Linux内核最多支持32个softirq,因为pending只有32bit*/
  8. pending = local_softirq_pending();
  9. account_system_vtime(current);
  1. __local_bh_disable((unsigned long)__builtin_return_address(0));
  2. lockdep_softirq_enter();
  3. cpu = smp_processor_id();
  4. restart:
  5. /* Reset the pending bitmask before enabling irqs */
  6. set_softirq_pending(0);/*获取了pending的softirq之后,清空所有pending的softirq的标志*/

local_irq_enable();

  1. h = softirq_vec;

do {

  1. if (pending & 1) { /*从最低位开始,循环右移逐位处理pending的softirq*/
  2. int prev_count = preempt_count();
  3. kstat_incr_softirqs_this_cpu(h - softirq_vec);

trace_softirq_entry(h, softirq_vec);

  1. h->action(h); /*执行softirq的处理函数*/
  2. trace_softirq_exit(h, softirq_vec);
  3. if (unlikely(prev_count != preempt_count())) {
  4. printk(KERN_ERR "huh, entered softirq %td %s %p"
  5. "with preempt_count %08x,"
  6. " exited with %08x?/n", h - softirq_vec,
  7. softirq_to_name[h - softirq_vec],
  8. h->action, prev_count, preempt_count());
  9. preempt_count() = prev_count;
  10. }

rcu_bh_qs(cpu);

  1. }
  2. h++;
  3. pending >>= 1; /*循环右移*/
  4. } while (pending);

local_irq_disable();

  1. pending = local_softirq_pending();
  2. if (pending && --max_restart) /*启动ksoftirqd的阈值*/
  3. goto restart;
  4. if (pending) /*启动ksoftirqd去处理softirq,此时说明pending的softirq比较多,比较频繁,上面的处理过程中,又不断有softirq被pending*/
  5. wakeup_softirqd();

lockdep_softirq_exit();

  1. account_system_vtime(current);
  2. _local_bh_enable();
三、使用softirq
softirq一般用在对实时性要求比较强的地方,当前的Linux内核中,只有两个子系统直接使用了softirq:网络子系统和块设备子系统。另外,增加新的softirq需要重新编译内核,因此,除非必须需要,最好考虑tasklet和kernel timer是否适合当前需要。
如果必须需要使用softirq,那么需要考虑的一个重要的问题就是新增加的softirq的优先级,默认情况下,softirq的数值越小优先级越高,根据实际经验,新增加的softirq最好在BLOCK_SOFTIRQ和TASKLET_SOFTIRQ之间。
softirq的处理函数通过open_softirq进行注册,此函数接收两个参数,一个是softirq的整数索引,另一个是该softirq对应的处理函数。例如在网络子系统中,注册了如下两个softirq及其处理函数:
    open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);
前面提到,软中断处理函数注册后,还需要将该软中断激活,此软中断才能被执行,激活操作是通过raise_softirq函数来实现,在网络子系统中激活代码如下:
  1. /* Called with irq disabled */
  2. static inline void ____napi_schedule(struct softnet_data *sd,
  3. struct napi_struct *napi)
  4. {
  5. list_add_tail(&napi->poll_list, &sd->poll_list);
  6. __raise_softirq_irqoff(NET_RX_SOFTIRQ);
  7. }
这里的__raise_softirq_irqoff和raise_softirq的区别是,前者在事先已经关中断的情况下可以被使用,后者自己完成中断的关闭和恢复。
分享到:
评论

相关推荐

    Linux的内核软中断(softirq)执行分析.zip_leavinghzf_linux_theory989

    linux软中断,分析内核中中断的流程以及怎样修改这部分内容,经过调试可以使用

    Linux软中断softirq机制流程图

    这个流程图粗略地描述了softirq, tasklet, bottomhalt, task queue这些对象之间的联系及调用流程。 主要依据:《Linux内核的Softirq机制》和《软中断概况》 图中可能存在错误,希望您的指正!

    Linux中断(interrupt)子系统之一:软件中断(softIRQ).docx

    Linux中断(interrupt)子系统之一:软件中断(softIRQ).docx

    locking-selftest-spin-softirq.rar_decide

    provide all the vectors, so that EQ creation response can decide which one to use.

    locking-selftest-spin-softirq.rar_Extras

    Asus Eee PC extras for Linux v2.13.6.

    linux操作系统内核技术-uestc课件

     6中断处理程序被分解为top half和bottom half的原因,介绍linux的softirq,tasklet,ksoftirqd和work queue,分析进程与top half,bottom half的竞争情形和同步。(4小时)  7掌握内核同步原理和方法:原子操作,...

    linux时间子系统

    对linux时间子系统研究,整理出的文档 可以作为手册进行查阅

    Linux 系统内核空间与用户空间通信的实现与分析

    Linux 系统内核空间与用户空间通信的实现与分析 Linux 内核模块的运行环境与传统进程间通信 在一台运行 Linux 的计算机中,CPU 在任何时候只会有如下四种状态: 【1】 在处理一个硬中断。 【2】 在处理一个软中断,...

    简单谈谈Linux内核定时器

    实质上,时钟中断处理程序会 换起TIMER_SOFTIRQ软中断 ,运行当前处理器上到期的所有定时器。 总结起来还是软中断的流程 a.注册软中断处理函数 /*/linux/kernel.timer.c*/ void __init init_timers(void) -->...

    Linux2.6内核标准教程(共计8-- 第1个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第6个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第3个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第4个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第2个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第7个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第5个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第8个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    irqflags-tracing中断标志状态追踪1

    the "irq-flags tracing" feature "traces" hardirq and softirq state, inthat it gi

    tibeecompare:用于比较执行跟踪的应用程序

    先决条件构建系统的SCons建造 ./bootstrap.sh./configuremake必需的事件此工具需要具有以下事件的跟踪: 标准 LTTng 事件: sched_ttwu 调度开关sched_process_fork sched_process_exec sched_process_exit softirq_...

    win-collector:夜莺的Windows代理

    以下仅列出差异部分cpumetriclinuxwindowscpu.softirq支持不支持cpu.steal支持不支持cpu.iowait支持不支持cpu.nice支持不支持cpu.guest支持不支持cpu.core.softirq支持不支持cpu.core.steal支持不支持cpu.core.io...

Global site tag (gtag.js) - Google Analytics