linux动态时钟探索
struct tick_sched {
struct hrtimer sched_timer;//用于实现时钟的定时器
unsigned long check_clocks;
enum tick_nohz_mode nohz_mode;
ktime_t idle_tick;//禁用周期时钟之前,上一个时钟信号到期时间。
int inidle;
int tick_stopped;//周期时钟是否已经停用,若停用,则置为1
unsigned long idle_jiffies;//存储周期时钟禁用时的jiffy值
unsigned long idle_calls;//内核试图停用周期时钟次数。
unsigned long idle_sleeps;//成功停用周期时钟次数。
int idle_active;
ktime_t idle_entrytime;
ktime_t idle_waketime;
ktime_t idle_exittime;
ktime_t idle_sleeptime;//周期时钟上一次禁用的准确时间
ktime_t idle_lastupdate;
ktime_t sleep_length;//周期时钟禁用的时间长度
unsigned long last_jiffies;
unsigned long next_jiffies;//下一个定时器到期的jiffy值
ktime_t idle_expires;//下一个将到期的经典定时器到期时间的jiffy值
int do_timer_last;
}
二、低分辨率下的动态时钟
static void tick_nohz_switch_to_nohz(void)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
ktime_t next;
if (!tick_nohz_enabled)//若没有启动动态时钟直接返回
return;
local_irq_disable();
if (tick_switch_to_oneshot(tick_nohz_handler)) {//切换时钟设备的处理函数为tick_nohz_switch_to_nohz
local_irq_enable();
return;
}
ts->nohz_mode = NOHZ_MODE_LOWRES;//设置为低分辨率
/*
* Recycle the hrtimer in ts, so we can share the
* hrtimer_forward with the highres code.
*/
//更新jiffy值
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
/* Get the next period */
next = tick_init_jiffy_update();
//设置下一个时钟事件
for (;;) {
hrtimer_set_expires(&ts->sched_timer, next);
if (!tick_program_event(next, ))
break;
next = ktime_add(next, tick_period);
}
local_irq_enable();
printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n",
smp_processor_id());
}
动态时钟处理程序tick_nohz_handler的实现如下:
static void tick_nohz_handler(struct clock_event_device *dev)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
struct pt_regs *regs = get_irq_regs();
int cpu = smp_processor_id();
ktime_t now = ktime_get();
dev->next_event.tv64 = KTIME_MAX;
//设置当前cpu负责全局时钟设备
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
tick_do_timer_cpu = cpu;
//若是全局时钟设备,则更新jiffy值
if (tick_do_timer_cpu == cpu)
tick_do_update_jiffies64(now);
//若是启动禁用全局时钟,则更新watchdog的时间戳
if (ts->tick_stopped) {
touch_softlockup_watchdog();
ts->idle_jiffies++;
}
update_process_times(user_mode(regs));
profile_tick(CPU_PROFILING);
//定时下一个时钟周期,并且更新jiffy
while (tick_nohz_reprogram(ts, now)) {
now = ktime_get();
tick_do_update_jiffies64(now);
}
}
static void tick_do_update_jiffies64(ktime_t now)
{
......
//更新经过的jiffy时间,判断是否在一个周期内,若是则直接返回
delta = ktime_sub(now, last_jiffies_update);
if (delta.tv64 < tick_period.tv64)
return;
//计算经过的jiffy差值,判断是否大于一个周期,若是大于,则先更新一个周期的jiffy值
delta = ktime_sub(now, last_jiffies_update);
if (delta.tv64 >= tick_period.tv64) {
delta = ktime_sub(delta, tick_period);
last_jiffies_update = ktime_add(last_jiffies_update,
tick_period);
//若是大于一个周期,则再计算差值,再加上这个差值的jiffy值
/* Slow path for long timeouts */
if (unlikely(delta.tv64 >= tick_period.tv64)) {
s64 incr = ktime_to_ns(tick_period);
ticks = ktime_divns(delta, incr);
last_jiffies_update = ktime_add_ns(last_jiffies_update,
incr * ticks);
}
do_timer(++ticks);
//更新下一个周期的jiffy值
/* Keep the tick_next_period variable up to date */
tick_next_period = ktime_add(last_jiffies_update, tick_period);
}
write_sequnlock(&xtime_lock);
}
#ifdef CONFIG_NO_HZ
/*
* Check if the do_timer duty was dropped. We don't care about
* concurrency: This happens only when the cpu in charge went
* into a long sleep. If two cpus happen to assign themself to
* this duty, then the jiffies update is still serialized by
* xtime_lock.
*/
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
tick_do_timer_cpu = cpu;
#endif
#ifdef CONFIG_NO_HZ
if (tick_nohz_enabled)
ts->nohz_mode = NOHZ_MODE_HIGHRES;
#endif
void cpu_idle(void)
{
......
/* endless idle loop with no priority at all */
while () {
tick_nohz_stop_sched_tick();//停止时钟
while (!need_resched()) {
......
//pm_idle的实现依赖具体架构而定,x86支持的一种实现是mwait实现,这种实现真正的是haunt住cpu,cpu切实不运转了,可以实现节能的目的,而ARM上可以使用wfi指令,cpu也haunt住,通过中断可以唤醒。
/* Don't trace irqs off for idle */
stop_critical_timings();
pm_idle();//节能的关键部分
start_critical_timings();
}
tick_nohz_restart_sched_tick();//启动时钟
preempt_enable_no_resched();
schedule();//调度切换
preempt_disable();
}
}
static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
{
ktime_t next;
tick_do_periodic_broadcast();
/*
* The device is in periodic mode. No reprogramming necessary:
*/
if (dev->mode == CLOCK_EVT_MODE_PERIODIC)//必须为oneshot模式
return;
/*
* Setup the next period for devices, which do not have
* periodic mode. We read dev->next_event first and add to it
* when the event alrady expired. clockevents_program_event()
* sets dev->next_event only when the event is really
* programmed to the device.
*/
for (next = dev->next_event; ;) {
next = ktime_add(next, tick_period);
//重新编程下一个事件
if (!clockevents_program_event(dev, next, ktime_get()))
return;
tick_do_periodic_broadcast();//处理本cpu事件和向其他cpu发送ipi中断,从而调用其他cpu的事件处理程序
}
}
tick_do_periodic_broadcast --》tick_do_broadcast ,而tick_do_broadcast是关键,其实现如下:
static void tick_do_broadcast(struct cpumask *mask)
{
int cpu = smp_processor_id();
struct tick_device *td;
//调用本cpu的事件处理程序
if (cpumask_test_cpu(cpu, mask)) {
cpumask_clear_cpu(cpu, mask);
td = &per_cpu(tick_cpu_device, cpu);
td->evtdev->event_handler(td->evtdev);
}
if (!cpumask_empty(mask)) {
//向其他cpu发送ipi中断
td = &per_cpu(tick_cpu_device, cpumask_first(mask));
td->evtdev->broadcast(mask);
}
}
static void lapic_timer_broadcast(const struct cpumask *mask)
{
#ifdef CONFIG_SMP
apic->send_IPI_mask(mask, LOCAL_TIMER_VECTOR);
#endif
}
最终ipi中断导致其他cpu调用本cpu的时钟事件设备的处理函数。
linux动态时钟探索的更多相关文章
- Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)
在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由一个周期时钟提供,不管系统中的clock_event_device是工作于周期触发模式,还是工作于单触发模式,也不管定时器系统是工 ...
- Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)【转】
转自:http://blog.csdn.net/droidphone/article/details/8112948 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 数据结 ...
- Linux动态频率调节系统CPUFreq之二:核心(core)架构与API
上一节中,我们大致地讲解了一下CPUFreq在用户空间的sysfs接口和它的几个重要的数据结构,同时也提到,CPUFreq子系统把一些公共的代码逻辑组织在一起,构成了CPUFreq的核心部分,这些公共 ...
- js 动态时钟
js 动态时钟 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www ...
- 使用Timer类的两个实例 动态时钟
package chapter16; import javax.swing.*; import chapter15.StillClock; import java.awt.event.*; publi ...
- linux 实时时钟(RTC)驱动【转】
转自:http://blog.csdn.net/yaozhenguo2006/article/details/6820218 这个是linux内核文档关于rtc实时时钟部分的说明,此文档主要描述了rt ...
- Linux 系统时钟(date) 硬件时钟(hwclock)
/********************************************************************* * Linux 系统时钟(date) 硬件时钟(hwclo ...
- 24小时学通Linux内核--内核探索工具类
寒假闲下来了,可以尽情的做自己喜欢的事情,专心待在实验室里燥起来了,因为大二的时候接触过Linux,只是关于内核方面确实是不好懂,所以十天的时间里还是希望能够补充一下Linux内核相关知识,接下来继续 ...
- linux动态库默认搜索路径设置的三种方法
众所周知, Linux 动态库的默认搜索路径是 /lib 和 /usr/lib .动态库被创建后,一般都复制到这两个目录中.当程序执行时需要某动态库, 并且该动态库还未加载到内存中,则系统会自动到这两 ...
随机推荐
- 读javascript高级程序设计17-在线检测,cookie,子cookie
一.在线状态检测 开发离线应用时,往往在离线状态时把数据存在本地,而在联机状态时再把数据发送到服务器.html5提供了检测在线状态的方法:navigator.onLine和online/offline ...
- C++ 读取txt文本内容,并将结果保存到新文本
循序渐进学习读文件 // readFile.cpp : 定义控制台应用程序的入口点. #include "stdafx.h" #include <iostream> # ...
- MD5加密代码
import java.security.MessageDigest;public class MD5_tes { public final static String MD5(String s){ ...
- 禁止浏览.htaccess文件
很多黑客会攻击和利用.htaccess做网站跳转,所以保护好.htaccess文件尤为重要. 在apache的httpd.conf的配置文件中 默认是已经设置了禁止对.htaccess的访问,截取相关 ...
- Java条件语句练习
/*System.out.println("请输入三个数字:");//输入三个数字,返回最大的那个. int a,b,c,big; Scanner d = new Scanner( ...
- 个人c语言编程风格总结
总结一下我个人的编程风格及这样做的原因吧,其实是为了给实验室写一个统一的C语言编程规范才写的.首先声明,我下面提到的编程规范,是自己给自己定的,不是c语言里面规定的. 一件事情,做成和做好中间可能隔了 ...
- Android图片压缩(质量压缩和尺寸压缩)
文章地址:::: http://blog.csdn.net/jdsjlzx/article/details/44228935
- MongoDB副本集的实现与维护实战
1.建立MongoDB副本集 现利用一台机器完成MongoDB副本集的建立 机器1:127.0.0.1:27017 机器2:127.0.0.1:27018 机器3:127.0.0.1:27019 在D ...
- OC基础--类的本质
类的本质: 类的本质其实也是一个对象(类对象),只要有了类对象, 将来就可以通过类对象来创建实例对象 程序中第一次使用该类的时候被创建,在整个程序中只有一份.此后每次使用都是这个类对象,它在程序运行时 ...
- C#注册表的读,写,删除,查找
首先分享一下写入,这个最常用的 public bool WriteRegedit() { try { RegistryKey rk = ...