linux动态时钟探索
-
1 struct tick_sched { 2 struct hrtimer sched_timer;//用于实现时钟的定时器 3 unsigned long check_clocks; 4 enum tick_nohz_mode nohz_mode; 5 ktime_t idle_tick;//禁用周期时钟之前,上一个时钟信号到期时间。 6 int inidle; 7 int tick_stopped;//周期时钟是否已经停用,若停用,则置为1 8 unsigned long idle_jiffies;//存储周期时钟禁用时的jiffy值 9 unsigned long idle_calls;//内核试图停用周期时钟次数。 10 unsigned long idle_sleeps;//成功停用周期时钟次数。 11 int idle_active; 12 ktime_t idle_entrytime; 13 ktime_t idle_waketime; 14 ktime_t idle_exittime; 15 ktime_t idle_sleeptime;//周期时钟上一次禁用的准确时间 16 ktime_t idle_lastupdate; 17 ktime_t sleep_length;//周期时钟禁用的时间长度 18 unsigned long last_jiffies; 19 unsigned long next_jiffies;//下一个定时器到期的jiffy值 20 ktime_t idle_expires;//下一个将到期的经典定时器到期时间的jiffy值 21 int do_timer_last; 22 }
二、低分辨率下的动态时钟
-
1 static void tick_nohz_switch_to_nohz(void) 2 { 3 struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); 4 ktime_t next; 5 if (!tick_nohz_enabled)//若没有启动动态时钟直接返回 6 return; 7 local_irq_disable(); 8 if (tick_switch_to_oneshot(tick_nohz_handler)) {//切换时钟设备的处理函数为tick_nohz_switch_to_nohz 9 local_irq_enable(); 10 return; 11 } 12 ts->nohz_mode = NOHZ_MODE_LOWRES;//设置为低分辨率 13 /* 14 * Recycle the hrtimer in ts, so we can share the 15 * hrtimer_forward with the highres code. 16 */ 17 //更新jiffy值 18 hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); 19 /* Get the next period */ 20 next = tick_init_jiffy_update(); 21 //设置下一个时钟事件 22 for (;;) { 23 hrtimer_set_expires(&ts->sched_timer, next); 24 if (!tick_program_event(next, 0)) 25 break; 26 next = ktime_add(next, tick_period); 27 } 28 local_irq_enable(); 29 printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", 30 smp_processor_id()); 31 }
-
1 static void tick_nohz_handler(struct clock_event_device *dev) 2 { 3 struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); 4 struct pt_regs *regs = get_irq_regs(); 5 int cpu = smp_processor_id(); 6 ktime_t now = ktime_get(); 7 dev->next_event.tv64 = KTIME_MAX; 8 //设置当前cpu负责全局时钟设备 9 if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE)) 10 tick_do_timer_cpu = cpu; 11 //若是全局时钟设备,则更新jiffy值 12 if (tick_do_timer_cpu == cpu) 13 tick_do_update_jiffies64(now); 14 //若是启动禁用全局时钟,则更新watchdog的时间戳 15 if (ts->tick_stopped) { 16 touch_softlockup_watchdog(); 17 ts->idle_jiffies++; 18 } 19 update_process_times(user_mode(regs)); 20 profile_tick(CPU_PROFILING); 21 //定时下一个时钟周期,并且更新jiffy 22 while (tick_nohz_reprogram(ts, now)) { 23 now = ktime_get(); 24 tick_do_update_jiffies64(now); 25 } 26 }
-
1 static void tick_do_update_jiffies64(ktime_t now) 2 { 3 ...... 4 //更新经过的jiffy时间,判断是否在一个周期内,若是则直接返回 5 delta = ktime_sub(now, last_jiffies_update); 6 if (delta.tv64 < tick_period.tv64) 7 return; 8 //计算经过的jiffy差值,判断是否大于一个周期,若是大于,则先更新一个周期的jiffy值 9 delta = ktime_sub(now, last_jiffies_update); 10 if (delta.tv64 >= tick_period.tv64) { 11 delta = ktime_sub(delta, tick_period); 12 last_jiffies_update = ktime_add(last_jiffies_update, 13 tick_period); 14 //若是大于一个周期,则再计算差值,再加上这个差值的jiffy值 15 /* Slow path for long timeouts */ 16 if (unlikely(delta.tv64 >= tick_period.tv64)) { 17 s64 incr = ktime_to_ns(tick_period); 18 ticks = ktime_divns(delta, incr); 19 last_jiffies_update = ktime_add_ns(last_jiffies_update, 20 incr * ticks); 21 } 22 do_timer(++ticks); 23 //更新下一个周期的jiffy值 24 /* Keep the tick_next_period variable up to date */ 25 tick_next_period = ktime_add(last_jiffies_update, tick_period); 26 } 27 write_sequnlock(&xtime_lock); 28 }
-
1 #ifdef CONFIG_NO_HZ 2 /* 3 * Check if the do_timer duty was dropped. We don‘t care about 4 * concurrency: This happens only when the cpu in charge went 5 * into a long sleep. If two cpus happen to assign themself to 6 * this duty, then the jiffies update is still serialized by 7 * xtime_lock. 8 */ 9 if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE)) 10 tick_do_timer_cpu = cpu; 11 #endif
1 #ifdef CONFIG_NO_HZ
2 if (tick_nohz_enabled)
3 ts->nohz_mode = NOHZ_MODE_HIGHRES;
4 #endif
-
1 void cpu_idle(void) 2 { 3 ...... 4 /* endless idle loop with no priority at all */ 5 while (1) { 6 tick_nohz_stop_sched_tick(1);//停止时钟 7 while (!need_resched()) { 8 ...... 9 //pm_idle的实现依赖具体架构而定,x86支持的一种实现是mwait实现,这种实现真正的是haunt住cpu,cpu切实不运转了,可以实现节能的目的,而ARM上可以使用wfi指令,cpu也haunt住,通过中断可以唤醒。 10 /* Don‘t trace irqs off for idle */ 11 stop_critical_timings(); 12 pm_idle();//节能的关键部分 13 start_critical_timings(); 14 } 15 tick_nohz_restart_sched_tick();//启动时钟 16 preempt_enable_no_resched(); 17 schedule();//调度切换 18 preempt_disable(); 19 } 20 }
-
1 static void tick_handle_periodic_broadcast(struct clock_event_device *dev) 2 { 3 ktime_t next; 4 tick_do_periodic_broadcast(); 5 /* 6 * The device is in periodic mode. No reprogramming necessary: 7 */ 8 if (dev->mode == CLOCK_EVT_MODE_PERIODIC)//必须为oneshot模式 9 return; 10 /* 11 * Setup the next period for devices, which do not have 12 * periodic mode. We read dev->next_event first and add to it 13 * when the event alrady expired. clockevents_program_event() 14 * sets dev->next_event only when the event is really 15 * programmed to the device. 16 */ 17 for (next = dev->next_event; ;) { 18 next = ktime_add(next, tick_period); 19 //重新编程下一个事件 20 if (!clockevents_program_event(dev, next, ktime_get())) 21 return; 22 tick_do_periodic_broadcast();//处理本cpu事件和向其他cpu发送ipi中断,从而调用其他cpu的事件处理程序 23 } 24 }
-
1 static void tick_do_broadcast(struct cpumask *mask) 2 { 3 int cpu = smp_processor_id(); 4 struct tick_device *td; 5 //调用本cpu的事件处理程序 6 if (cpumask_test_cpu(cpu, mask)) { 7 cpumask_clear_cpu(cpu, mask); 8 td = &per_cpu(tick_cpu_device, cpu); 9 td->evtdev->event_handler(td->evtdev); 10 } 11 if (!cpumask_empty(mask)) { 12 //向其他cpu发送ipi中断 13 td = &per_cpu(tick_cpu_device, cpumask_first(mask)); 14 td->evtdev->broadcast(mask); 15 } 16 }
-
1 static void lapic_timer_broadcast(const struct cpumask *mask) 2 { 3 #ifdef CONFIG_SMP 4 apic->send_IPI_mask(mask, LOCAL_TIMER_VECTOR); 5 #endif 6 }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。