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 .动态库被创建后,一般都复制到这两个目录中.当程序执行时需要某动态库, 并且该动态库还未加载到内存中,则系统会自动到这两 ...
随机推荐
- WebAPI返回数据类型解惑
本文来自:http://www.cnblogs.com/lzrabbit/archive/2013/03/19/2948522.html 最近开始使用WebAPI,上手很容易,然后有些疑惑 1.Web ...
- C++11新特性学习
http://www.cprogramming.com/c++11/c++11-lambda-closures.html
- 错误 java.lang.ClassCastException: com.ylpw.sms.YZZYSenderUtil cannot be cast to ResourceBundle
出现错误: java.lang.ClassCastException: com.ylpw.sms.YZZYSenderUtil cannot be cast to ResourceBundle 百度搜 ...
- POJ 3278 Catch That Cow
注:本人英语很渣,题目大意大多来自百度~=0= 题目大意 农民约翰需要抓住他的牛,他和他的牛在一条直线上(估计是一维生物),约翰在N (0 ≤ N ≤ 100,000)处,他的牛在 K (0 ≤ K ...
- solr索引服务器的配置和solrj集成开发总结
一.环境:solr6.2 + jdk1.8 + tomcat8 (solr不同版本需要最低的环境不同) solr6 需要至少jdk1.8 .对应的solr5+jdk1.7+tomcat7 实测 ...
- ubuntu命令
chmod 777.755 root权限 rm 删除文件 rm -rf 删除文件夹 ctrl+o 保存 ctrl+x 退出 :wq 保存并退出 查看一台服务器上面哪些服务及端口 netstat -l ...
- repcache实现memcached主从
1.repcached介绍 repcached是日本人开发的实现memcached复制功能,它是一个单 master单 slave的方案,但它的 master/slave都是可读写的,而且可以相互同步 ...
- Ubuntu上安装MySql过程,以及遇到的一些问题
今天在Ubuntu服务器上安装MySql的时候遇到了一些问题,记录下来,以防以后忘记. 安装环境:Ubuntu14.04 安装命令: //安装Mysal服务端//会提示输入root密码 sudo ap ...
- 前端:IE兼容性的相关方法
有一段时间做前端的时候,IE下的就兼容性是比较令人头痛的问题,我在这一过程中也是看了很多的资料,然后把一些自己觉得比较普遍的问题进行一下相关的总结. 1.在IE6下,格式为png的图片在IE6上的透明 ...
- 场景7 Data Guard
场景7 Data Guard 官方文档 :Oracle Data Guard Concepts and Administration 用于数据容灾,通过主备库同步(主库将redo日志传送到备库,一个 ...