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 .动态库被创建后,一般都复制到这两个目录中.当程序执行时需要某动态库, 并且该动态库还未加载到内存中,则系统会自动到这两 ...
随机推荐
- css 伪元素分享!!!
最近接触到的css 伪元素觉得还算不错 分享下: 1.清楚内盒浮动设置: ;} .back_list ul:after{;visibility: hidden;}/*清楚内盒浮动设置*/ 2.伪元素a ...
- Android开发--Android Studio配置
1.常见问题 emulator: You might want to adjust your AVD RAM size and/or HAXM configuration to run in fast ...
- guava学习--Preconditions
转载:https://my.oschina.net/realfighter/blog/349819 Preconditions是guava提供的用于进行代码校验的工具类,其中提供了许多重要的静态校验方 ...
- Windows Store App 全球化:应用中设置语言选项
当开发者将开发的应用上传到Windows应用商店以后,使用Windows 8系统的用户可能会看到并下载这些应用,而这些用户所在的区域或者所使用的语言可能都不相同,如果他们在使用应用程序时希望改变应用显 ...
- 在VMware中安装ubuntu出现菜单栏无法显示的情况
在VMware中安装ubuntu出现菜单栏无法显示的情况 其实这个问题的原因时由于VMware中enable了3D图形加速界面,只需要shutdown当前运行的虚拟机,然后在虚拟机,设置,显示器,3D ...
- k8s volume
只有nfs和rbd的,本人翻译确实很渣 在容器中磁盘文件寿命是短暂的,当在容器中运行一些重要程序时,这会产生一些问题. 首先,当一个容器崩溃后,kubelet将重新启动该容器, ...
- windows8.1下php环境搭建及基本配置(php+apache+mysql)
一.php下载安装:php-5.6.1-Win32-VC11-x64.zip.解压,操作: 1.复制php.ini-production,更名为php.ini 2.在环境变量PATH末尾添加:D:\p ...
- @autoreleasepool在MRC和ARC中的区别
对于@autoreleasepool {} (1)在ARC中会销毁所有在里面创建的对象,即使你用外面的Strong指针指向他 (2)在MRC中如果有外部的强指针指向,不会销毁对象,retainCoun ...
- Linux学习 :中断处理机制 & poll机制
中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务 的程序中去,服务完毕后再返回去继续运行被暂时中断的程序 ...
- 使用Volley执行网络数据传输
首先需要实例化一个RequestQueue RequestQueue queue = Volley.newRequestQueue(this); 然后是根据提供的URL请求字符串响应 String u ...