浅析linux内核中timer定时器的生成和sofirq软中断调用流程【转】

简介:

转自:http://blog.chinaunix.net/uid-20564848-id-73480.html

浅析linux内核中timer定时器的生成和sofirq软中断调用流程

  mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(&base->lock);禁止cpu中断,所以我们的timer回调处理函数handler工作在irq关闭的环境中,所以需要作很多考虑,比如在handler中尽量不要执行会引起pending的函数调用,比如kmalloc之类可能引起pending的操作,否则会使kernel永远停在我们的handler中不能返回,这样kernel将因为我们ko设计上的失败而死机[luther.gliethttp]!
  我们可以使用如下几行语句,向我们的ko驱动添加一个timer定时器,来处理时间事件:
struct __wlanwlan_check_tx_flow_timer
{
    struct timer_list timer;
    int timer_freq;
} wlan_check_tx_flow_timer {
        .timer_freq = 8*1000,
};
static void wlan_check_tx_flow_timer_handler(unsigned long data)
{
    ...
    //重新启动timer定时器 
    mod_timer(&wlan_check_tx_flow_timer.timer, jiffies + msecs_to_jiffies(wlan_check_tx_flow_timer.timer_freq));
    ...
}
//设置定时器
setup_timer(&wlan_check_tx_flow_timer.timer, wlan_check_tx_flow_timer_handler(unsigned long)&wlan_check_tx_flow_timer);
//添加定时器
mod_timer(&wlan_check_tx_flow_timer.timer, jiffies + msecs_to_jiffies(wlan_check_tx_flow_timer.timer_freq));

那么这个wlan_check_tx_flow_timer_handler处理函数在什么时候被调用的呢?那么我们追入内核中,看看kernel对定时器的具体管理.
首先kernel在启动的最前面注册TIMER_SOFTIRQ的处理函数[luther.gliethttp],
start_kernel
=>init_timers
=>open_softirq(TIMER_SOFTIRQ, run_timer_softirqNULL);
那么由谁来调用raise_softirq(TIMER_SOFTIRQ);触发TIMER_SOFTIRQ软中断呢,这就和平台相关了,对于pxa935处理器来说[luther.gliethttp],
MACHINE_START(LUTHER"luther")
    .phys_io = 0x40000000,
    .boot_params = 0xa0000100,
    .io_pg_offst (io_p2v(0x40000000>> 18& 0xfffc,
    .map_io = pxa_map_io,
    .init_irq = pxa3xx_init_irq,
    .timer &pxa_timer,
    .init_machine = luther_init,
MACHINE_END
=>pxa_timer_init//平台对应的定时器初始化
==>pxa_timer_irq.dev_id &ckevt_32ktimer;
==>setup_irq(IRQ_OST_4_11&pxa_timer_irq)//32768的rtc
==>clockevents_register_device(&ckevt_32ktimer);

pxa_timer_interrupt中断处理函数
=>c->event_handler(c);也就是tick_handle_periodic系统时钟函数
=>tick_handle_periodic
=>update_process_times
=>run_local_timers
=>raise_softirq(TIMER_SOFTIRQ);
这里仅仅是触发了TIMER_SOFTIRQ软中断,那么在什么地方处理我们mod_timer添加的timer定时器处理函数wlan_check_tx_flow_timer_handler呢[luther.gliethttp]?
__irq_svc://内核中发生的中断
__irq_usr://用户空间时发生的中断
=>asm_do_IRQ
=>irq_exit =>do_softirq =>__do_softirq =>调用上面注册的run_timer_softirq软中断处理函数 =>run_timer_softirq =>__run_timers static inline void __run_timers(struct tvec_base *base) {     struct timer_list *timer;     spin_lock_irq(&base->lock);//禁止中断     while (time_after_eq(jiffies, base->timer_jiffies)) {         ...         if (时间到了) {         ...         fn = timer->function;         data = timer->data;         fn(data);//这就是我们上面添加的static void wlan_check_tx_flow_timer_handler(unsigned long data);定时器处理函数了.         ...         }         ...     }     set_running_timer(base, NULL);     spin_unlock_irq(&base->lock);//打开中断 } //================ include/asm/hardirq.h typedef struct {     unsigned int __softirq_pending;     unsigned int local_timer_irqs; } ____cacheline_aligned irq_cpustat_t; //================ kernel/softirq.c|45| irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned; #ifndef __ARCH_IRQ_STAT irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;//在这里定义irq_stat存储空间 EXPORT_SYMBOL(irq_stat); #endif //================ include/linux/irq_cpustat.h #ifndef __ARCH_IRQ_STAT //引用的就是上面的irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned; extern irq_cpustat_t irq_stat[];        /* defined in asm/hardirq.h */ #define __IRQ_STAT(cpu, member)    (irq_stat[cpu].member) #endif //================ arch/arm/kernel/entry-armv.S|331| .word    irq_stat #ifdef CONFIG_PREEMPT svc_preempt:     teq    r8, #0                @ was preempt count = 0     ldreq    r6, .LCirq_stat //操作     movne    pc, lr                @ no     ldr    r0, [r6, #4]            @ local_irq_count     ldr    r1, [r6, #8]            @ local_bh_count     adds    r0, r0, r1     movne    pc, lr     mov    r7, #0                @ preempt_schedule_irq     str    r7, [tsk, #TI_PREEMPT]        @ expects preempt_count == 0 1:    bl    preempt_schedule_irq        @ irq en/disable is done inside     ldr    r0, [tsk, #TI_FLAGS]        @ get new tasks TI_FLAGS     tst    r0, #_TIF_NEED_RESCHED     beq    preempt_return            @ go again     b    1b #endif .LCirq_stat:     .word    irq_stat //引用irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;地址 #endif   /* arch independent irq_stat fields */ #define local_softirq_pending() \     __IRQ_STAT(smp_processor_id(), __softirq_pending) #define __ARCH_IRQ_EXIT_IRQS_DISABLED    1 #ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED # define invoke_softirq()    __do_softirq() //是这个 #else # define invoke_softirq()    do_softirq() #endif #ifndef __ARCH_SET_SOFTIRQ_PENDING #define set_softirq_pending(x) (local_softirq_pending() = (x)) #define or_softirq_pending(x) (local_softirq_pending() |= (x)) #endif #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) inline void raise_softirq_irqoff(unsigned int nr) {     __raise_softirq_irqoff(nr);         if (!in_interrupt())         wakeup_softirqd(); } void raise_softirq(unsigned int nr) {     unsigned long flags;     local_irq_save(flags);     raise_softirq_irqoff(nr);     local_irq_restore(flags); } =>s3c2410_timer_interrupt =>timer_tick =>pxa_timer_init ==>pxa_timer_irq.dev_id = &ckevt_32ktimer; ==>setup_irq(IRQ_OST_4_11, &pxa_timer_irq); //32768的rtc ==>clockevents_register_device(&ckevt_32ktimer); =>clockevents_register_device =>clockevents_do_notify =>raw_notifier_call_chain(&clockevents_chain, reason, dev); =>__raw_notifier_call_chain =>notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); =>nb->notifier_call(nb, val, v);就是tick_notify start_kernel =>tick_init static struct notifier_block tick_notifier = {     .notifier_call = tick_notify, }; void __init tick_init(void) {     clockevents_register_notifier(&tick_notifier); } clockevents_register_notifier =>raw_notifier_chain_register(&clockevents_chain, nb); =>notifier_chain_register将tick_notifier添加到clockevents_chain这个单向链表中[luther.gliethttp] static int tick_notify(struct notifier_block *nb, unsigned long reason,              void *dev) {     switch (reason) {     case CLOCK_EVT_NOTIFY_ADD:         return tick_check_new_device(dev);     ...     return NOTIFY_OK; } =>tick_notify =>tick_check_new_device =>tick_setup_device(td, newdev, cpu, cpumask); static void tick_setup_device(struct tick_device *td,              struct clock_event_device *newdev, int cpu,              cpumask_t cpumask) {     ktime_t next_event;     void (*handler)(struct clock_event_device *) = NULL;     /*      * First device setup ?      */     if (!td->evtdev) {         /*          * If no cpu took the do_timer update, assign it to          * this cpu:          */         if (tick_do_timer_cpu == -1) {             tick_do_timer_cpu = cpu;             tick_next_period = ktime_get();             tick_period = ktime_set(0, NSEC_PER_SEC / HZ);         }         /*          * Startup in periodic mode first.          */         td->mode = TICKDEV_MODE_PERIODIC;//设置第1个tick设备为TICKDEV_MODE_PERIODIC模式     } else {         handler = td->evtdev->event_handler;         next_event = td->evtdev->next_event;     }     td->evtdev = newdev;     ...     if (td->mode == TICKDEV_MODE_PERIODIC)         tick_setup_periodic(newdev, 0);     else         tick_setup_oneshot(newdev, handler, next_event); } void tick_setup_periodic(struct clock_event_device *dev, int broadcast) {     tick_set_periodic_handler(dev, broadcast);//设置event_handler处理函数为dev->event_handler = tick_handle_periodic;     /* Broadcast setup ? */     if (!tick_device_is_functional(dev))         return;     if (dev->features & CLOCK_EVT_FEAT_PERIODIC) {         clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);     } else {         unsigned long seq;         ktime_t next;         do {             seq = read_seqbegin(&xtime_lock);             next = tick_next_period;         } while (read_seqretry(&xtime_lock, seq));         clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);         for (;;) {             if (!clockevents_program_event(dev, next, ktime_get()))                 return;             next = ktime_add(next, tick_period);         }     } } void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast) {     if (!broadcast)         dev->event_handler = tick_handle_periodic;     else         dev->event_handler = tick_handle_periodic_broadcast; } =>pxa_timer_interrupt {     ...     if (OSSR & OST_C4) {         OIER &= ~OST_C4;         OSSR = OST_C4;         if (timer32k_enabled)             c->event_handler(c);//调用tick_handle_periodic处理函数,作为     }     ... } void tick_handle_periodic(struct clock_event_device *dev) {     int cpu = smp_processor_id();     ktime_t next;     tick_periodic(cpu);//调用do_timer(1);将jiffies_64加1     if (dev->mode != CLOCK_EVT_MODE_ONESHOT)         return;     /*      * Setup the next period for devices, which do not have      * periodic mode:      */     next = ktime_add(dev->next_event, tick_period);     for (;;) {         if (!clockevents_program_event(dev, next, ktime_get()))             return;         tick_periodic(cpu);         next = ktime_add(next, tick_period);     } } static void tick_periodic(int cpu) {     if (tick_do_timer_cpu == cpu) {         write_seqlock(&xtime_lock);         /* Keep track of the next tick event */         tick_next_period = ktime_add(tick_next_period, tick_period);         do_timer(1);         write_sequnlock(&xtime_lock);     }     update_process_times(user_mode(get_irq_regs()));     profile_tick(CPU_PROFILING); } arch/arm/kernel/time.c|332| update_process_times(user_mode(get_irq_regs())); =>update_process_times =>run_local_timers =>raise_softirq(TIMER_SOFTIRQ);//触发软中断,当irq_exit时调用__do_softirq来处理 =>run_timer_softirq =>__run_timers => fn = timer->function;//执行 data = timer->data; fn(data); //================ include/asm/arch-pxa/entry-macro.S|22| .macro    get_irqnr_and_base, irqnr, irqstat, base, tmp //pxa获取irq中断号函数 //================ arch/arm/kernel/entry-armv.S|37| bne    asm_do_IRQ     .macro    irq_handler     get_irqnr_preamble r5, lr 1:    get_irqnr_and_base r0, r6, r5, lr //获取irq中断号,存储到r0寄存器中,作为参数传递给asm_do_IRQ     movne    r1, sp     @     @ routine called with r0 = irq number, r1 = struct pt_regs *     @     adrne    lr, 1b     bne    asm_do_IRQ     ... //================     .align    5 __irq_svc://内核中发生的中断     svc_entry     ...     irq_handler     ... //================     .align    5 __irq_usr://用户空间时发生的中断     usr_entry     ...     irq_handler     ... //================     .macro    vector_stub, name, mode, correction=0     .align    5 vector_\name:     .if \correction     sub    lr, lr, #\correction     .endif     @     @ Save r0, lr_<exception> (parent PC) and spsr_<exception>     @ (parent CPSR)     @     stmia    sp, {r0, lr}        @ save r0, lr     mrs    lr, spsr     str    lr, [sp, #8]        @ save spsr     @     @ Prepare for SVC32 mode. IRQs remain disabled.     @     mrs    r0, cpsr     eor    r0, r0, #(\mode ^ SVC_MODE)     msr    spsr_cxsf, r0     @     @ the branch table must immediately follow this code     @     and    lr, lr, #0x0f //lr存储了spsr,所以一共有16种cpu模式     mov    r0, sp //传参     ldr    lr, [pc, lr, lsl #2]//取出相应模式下的处理函数指针,比如__irq_usr或者__irq_svc     movs    pc, lr            @ branch to handler in SVC mode     .endm //================     .globl    __stubs_start __stubs_start: /*  * Interrupt dispatcher  */     vector_stub    irq, IRQ_MODE, 4     .long    __irq_usr            @ 0 (USR_26 / USR_32)     .long    __irq_invalid        @ 1 (FIQ_26 / FIQ_32)     .long    __irq_invalid        @ 2 (IRQ_26 / IRQ_32)     .long    __irq_svc            @ 3 (SVC_26 / SVC_32)     .long    __irq_invalid            @ 4     .long    __irq_invalid            @ 5     .long    __irq_invalid            @ 6     .long    __irq_invalid            @ 7     .long    __irq_invalid            @ 8     .long    __irq_invalid            @ 9     .long    __irq_invalid            @ a     .long    __irq_invalid            @ b     .long    __irq_invalid            @ c     .long    __irq_invalid            @ d     .long    __irq_invalid            @ e     .long    __irq_invalid            @ f //================     .globl    __vectors_start __vectors_start:     swi    SYS_ERROR0     b    vector_und + stubs_offset     ldr    pc, .LCvswi + stubs_offset     b    vector_pabt + stubs_offset     b    vector_dabt + stubs_offset     b    vector_addrexcptn + stubs_offset     b    vector_irq + stubs_offset     b    vector_fiq + stubs_offset //================ asm_do_IRQ(unsigned int irq, struct pt_regs *regs) =>desc_handle_irq(irq, desc);// static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc) {     desc->handle_irq(irq, desc);//调用中断号irq对应的handler回调处理函数[luther.gliethttp] } __irq_svc://内核中发生的中断 __irq_usr://用户空间时发生的中断 =>asm_do_IRQ =>irq_exit =>do_softirq =>__do_softirq => {     ...     h = softirq_vec;//执行软中断函数     do {         if (pending & 1) {             h->action(h); //如果32768的时间到达,那asm_do_IRQ中将触发raise_softirq(TIMER_SOFTIRQ); //在这里将执行管理系统tick的run_timer_softirq软中断[luther.gliethttp]             rcu_bh_qsctr_inc(cpu);         }         h++;         pending >>= 1;     } while (pending);     ... } start_kernel =>init_timers =>open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL); void open_softirq(int nr, void (*action)(struct softirq_action*), void *data) {     softirq_vec[nr].data = data;     softirq_vec[nr].action = action; } static void run_timer_softirq(struct softirq_action *h) {     struct tvec_base *base = __get_cpu_var(tvec_bases);//获得time时间根     hrtimer_run_pending();     if (time_after_eq(jiffies, base->timer_jiffies))         __run_timers(base); } //执行软中断 =>run_timer_softirq =>__run_timers => fn = timer->function; data = timer->data; fn(data); static inline void __run_timers(struct tvec_base *base) {     ...     spin_lock_irq(&base->lock);//禁止中断     ...     fn = timer->function;     data = timer->data;     fn(data);     ...     set_running_timer(base, NULL);     spin_unlock_irq(&base->lock);//打开中断 } mod_timer =>__mod_timer int __mod_timer(struct timer_list *timer, unsigned long expires) {     struct tvec_base *base, *new_base;     unsigned long flags;     int ret = 0;     timer_stats_timer_set_start_info(timer);     BUG_ON(!timer->function);     base = lock_timer_base(timer, &flags);     if (timer_pending(timer)) {         detach_timer(timer, 0);         ret = 1;     }     new_base = __get_cpu_var(tvec_bases);//获得time时间根     if (base != new_base) {         /*          * We are trying to schedule the timer on the local CPU.          * However we can't change timer's base while it is running,          * otherwise del_timer_sync() can't detect that the timer's          * handler yet has not finished. This also guarantees that          * the timer is serialized wrt itself.          */         if (likely(base->running_timer != timer)) {             /* See the comment in lock_timer_base() */             timer_set_base(timer, NULL);             spin_unlock(&base->lock);             base = new_base;             spin_lock(&base->lock);             timer_set_base(timer, base);         }     }     timer->expires = expires;     internal_add_timer(base, timer); //添加到链表上,这样当timer超时到达时,run_timer_softirq=>__run_timers软中断中将会回调该处理函数[luther.gliethttp].     spin_unlock_irqrestore(&base->lock, flags);     return ret; }















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7246640.html,如需转载请自行联系原作者


相关文章
|
5天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
27 6
|
4天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
21 9
|
3天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
19 6
|
4天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
19 5
|
1天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
15 4
|
4天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
5天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
4天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
4天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
20 2
|
6天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####