9G10内核时钟tick实现
9G10中PIT(Periodic Interval Timer)提供OS调度中断,它提供了最高精度和最有效的管理(即使系统长时间响应)。
一. 硬件
PIT目标是提供OS的周期中断。PIT提供一个可编程溢出计数器和一个reset-on-read特性。它包含两个计数器:20bit CPIV counter和12bit PICNT(Periodic Interval) counter。两个计数器都工作在Master Clock/16。
CPIV从0增加到PIT_MR中PIV设定值,到达设定值后复位(0)且PICNT加1。假如中断是能,PIT_SR的PITS位置位,触发一个中断。
写一个新的PIV值到PITMR不会reset/restart计数器。
当读取PIT_PIVR(Periodic Interval Value Register)时,PICNT复位(0),且PITS清零(获取到一个中断)。PICNT表示自从上次读取PIT_PIVR后多少个Periodic Interval过去了。
当读取PIT_PIIR(Periodic Interval Image Register)时,对CPIV和PICNT无影响。
PIT可通过PIT_MR的PITEN位使能或关闭,仅当CPIV变为0时PITEN才生效。
PIT共有4个寄存器,分别是PIT_MR(Mode Register),PIT_SR(Status Register),PIT_PIVR(Periodic Interval Value Register),PIT_PIIR(Periodic Interval Image Register)。
PIT_MR: bit0-19 PIV, bit24 PITEN, bit25 PITIEN
PIT_SR:bit0 PITS
PIT_PIVR:bit0-19 CPIV,bit20-31 PICNT
PIT_PIIR:bit0-19 CPIV,bit20-31 PICNT
二. 软件
在板级文件中machine_desc中设置timer=&at91sam926x_timer,下面看下at91sam926x_timer的初始化函数。
at91sam926x_time.c
/*
* Set up both clocksource and clockevent support.
*/
static void __init at91sam926x_pit_init(void)
{
unsigned long pit_rate;
unsigned bits;
/*
* Use our actual MCK to figure out how many MCK/16 ticks per
* 1/HZ period (instead of a compile-time constant LATCH).
*/
pit_rate = clk_get_rate(clk_get(NULL, "mck")) / 16;
pit_cycle = (pit_rate + HZ/2) / HZ; //向上取整,四舍五入
WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0);
/* Initialize and enable the timer */
at91sam926x_pit_reset();
/*
* Register clocksource. The high order bits of PIV are unused,
* so this isn't a 32-bit counter unless we get clockevent irqs.
*/
/* mult = (10^9 << shift) / pit_rate */
pit_clk.mult = clocksource_hz2mult(pit_rate, pit_clk.shift);
bits = 12 /* PICNT */ + ilog2(pit_cycle) /* PIV */;
pit_clk.mask = CLOCKSOURCE_MASK(bits);
clocksource_register(&pit_clk); // hz或cycle到ns的转换
/* Set up irq handler */
setup_irq(AT91_ID_SYS, &at91sam926x_pit_irq);
/* Set up and register clockevents */
/* factor = (pit_rate << shift) / NSEC_PER_SEC */
pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift);
pit_clkevt.cpumask = cpumask_of(0);
clockevents_register_device(&pit_clkevt); // ns到hz或cycle的转换
}
static struct clocksource pit_clk = {
.name = "pit",
.rating = 175,
.read = read_pit_clk,
.shift = 20,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct clock_event_device pit_clkevt = {
.name = "pit",
.features = CLOCK_EVT_FEAT_PERIODIC,
.shift = 32,
.rating = 100,
.set_mode = pit_clkevt_mode,
};
Clocksource注册过程
Clocksource_register(&pit_clk); -->clocksource_enqueue(&pit_clk);
将pit_clk添加到系统时钟源list中。
Clockevents_register_device(&pit_clkevt);
-->clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, &pit_clkevt);
--> raw_notifier_call_chain(&clockevents_chain, reason, &pit_clkevt);
-->__raw_notifier_call_chain(nh, val, v, -1, NULL);
-->notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
-->nb->notifier_call(nb, val, v);
即nb->notifier_call((&clockevents_chain)->head, CLOCK_EVT_NOTIFY_ADD, &pit_clkevt);
Kernel/notifier.c
notifier_call()函数的初始化在start_kernel()的tick_init()中,后面分析。
-->tick_check_new_device(&pit_clkevt); //kernel/time/tick-common.c
确认clock_event_device的rating是否高于当前的,高于则替换当前时钟事件设备
若pit_clkevt->features&CLOCK_EVT_FEAT_ONESHOT 还要调用 tick_oneshot_notify()
-->tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
-->tick_setup_periodic(newdev, 0);
---->tick_set_periodic_handler(newdev, 0);
------>(&pit_clkevt)->event_handler = tick_handle_periodic;
// kernel/time/tick-common.c
即设置pit_clkevt的事件处理函数为tick_handle_periodic()。
---->clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
---->(&pit_clkevt)->set_mode(CLOCK_EVT_MODE_PERIODIC, &pit_clkevt);
pit_clkevt.mode = CLOCK_EVT_MODE_PERIODIC;
至此调用pit_clkevt的set_mode()函数,完成全部初始化工作
在init/main.c的start_kernel()中tick_init()调用过程:
tick_init(); //kernel/time/tick-common.c
-->clockevents_register_notifier(&tick_notifier);
-->raw_notifier_chain_register(&clockevents_chain, nb);
-->notifier_chain_register(&nh->head, n); // kernel/notifier.c
-->向clockevents_chian注册tick_notifier()
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);
case CLOCK_EVT_NOTIFY_BROADCAST_ON:
case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
tick_broadcast_on_off(reason, dev);
break;
case CLOCK_EVT_NOTIFY_BROADCAST_ENTER:
case CLOCK_EVT_NOTIFY_BROADCAST_EXIT:
tick_broadcast_oneshot_control(reason);
break;
case CLOCK_EVT_NOTIFY_CPU_DYING:
tick_handover_do_timer(dev);
break;
case CLOCK_EVT_NOTIFY_CPU_DEAD:
tick_shutdown_broadcast_oneshot(dev);
tick_shutdown_broadcast(dev);
tick_shutdown(dev);
break;
case CLOCK_EVT_NOTIFY_SUSPEND:
tick_suspend();
tick_suspend_broadcast();
break;
case CLOCK_EVT_NOTIFY_RESUME:
tick_resume();
break;
default:
break;
}
return NOTIFY_OK;
}
static struct notifier_block tick_notifier = {
.notifier_call = tick_notify,
};
即向系统注册了notifier_block结构体tick_notifier的notifier_call()回调函数。
中断处理过程
根据初始化过程,每个tick或Hz产生一次中断。
static struct irqaction at91sam926x_pit_irq = {
.name = "at91_tick",
.flags = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = at91sam926x_pit_interrupt
};
static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
{
/* The PIT interrupt may be disabled, and is shared */
if ((pit_clkevt.mode == CLOCK_EVT_MODE_PERIODIC)
&& (at91_sys_read(AT91_PIT_SR) & AT91_PIT_PITS)) {
unsigned nr_ticks;
/* Get number of ticks performed before irq, and ack it */
nr_ticks = PIT_PICNT(at91_sys_read(AT91_PIT_PIVR));
do {
pit_cnt += pit_cycle;
pit_clkevt.event_handler(&pit_clkevt);
nr_ticks--;
} while (nr_ticks);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
at91sam926x_pit_interrupt()
-->pit_clkevt.event_handler(&pit_clkevt);
-->tick_handle_periodic(&pit_clkevt); //kernel/time/tick-common.c
-->tick_periodic(cpu);
-->do_timer(1); //kernel/timer.c 更新jiffies_64和时间
-->update_times(ticks);
-->update_wall_time(); //更新xtime
void do_timer(unsigned long ticks)
{
jiffies_64 += ticks;
update_times(ticks);
}
static inline void update_times(unsigned long ticks)
{
update_wall_time();
calc_load(ticks);
}
三. 知识扩展
1)时钟系统中最重要的结构体变量:clockevent clocksource xtime。
clockevent为kernel提供了时钟中断的一些处理函数,特别是对于tickless系统,提供了设置下一次时钟中断时间点的接口set_next_event和timer模式设置接口set_mode。
clocksource则是kernel真正的计数者 时钟源,常规的时钟中断中的计数以及提高精度的补充计数都来自于clocksource。
clocksource成员rating代表了时钟精度,参考值如下:
1--99: 不适合于用作实际的时钟源,只用于启动过程或用于测试;
100--199:基本可用,可用作真实的时钟源,但不推荐;
200--299:精度较好,可用作真实的时钟源;
300--399:很好,精确的时钟源;
400--499:理想的时钟源,如有可能就必须选择它作为时钟源;
xtime是kernel的墙上时间,记录从1970-1-1至今的时间差,不管是用户空间还是内核空间要获取的系统时间都需要去读取xtime。
2)timer中断模式
kernel下timer的中断模式支持2种:周期性和一次性,也就是periodic和oneshot。
对于固定tick(1/HZ)系统的timer使用periodic。但是对于tickless系统,则必须要使用oneshot 模式了,因为每次timer中断间隔不一样长。
3系统启动时读取硬件时间,一般在rtc驱动加载后,调用rtc_hctosys()(driver/rtc/hctosys.c)。其根据内核配置CONFIG_RTC_HCTOSYS来决定是否同步时间,并读取CONFIG_RTC_HCTOSYS_DEVICE(一般为rtc0)来获取RTC时间。
late_initcall(rtc_hctosys);
参考:
http://blog.csdn.net/skyflying2012/article/details/44727409
9G10内核时钟tick实现的更多相关文章
- MINIX3 内核时钟分析
MINIX3 内核时钟分析 4.1 内核时钟概要 先想想为什么 OS 需要时钟?时钟是异步的一个非常重要的标志,设想一下,如 果我们的应用程序需要在多少秒后将触发某个程序或者进程,我们该怎么做到? ...
- adjtimex和时钟的几个概念tick,freq,ppm,jiffies
adjtimex使用 今天遇到一个ntp的同步问题.服务器上配置好了ntpd,在启动前也手动进行过同步,但是过段时间ntpq查询发现服务器即便能选出同步服务器,但是系统的时间偏差越来越大. 服务器上实 ...
- ARM Linux系统的时钟机制
1. Linux下有两类时钟: 1.1 实时钟RTC 它由板上电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,硬件时钟.当操作系统关机的时候,用这个来记录时间,但是对于运行 ...
- stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】
stm32可选的时钟源 在STM32中,可以用内部时钟,也可以用外部时钟,在要求进度高的应用场合最好用外部晶体震荡器,内部时钟存在一定的精度误差. 准确的来说有4个时钟源可以选分别是HSI.LSI.H ...
- Linux的时钟管理
本文转自博客园zhenwenxian的Linux时间管理,很详细,写得很不错,对初学者还是有很大帮助的. 时间管理在内核中占有非常重要的地位.相对于事件驱动,内核中有大量的函数都是基于时间驱动的.内核 ...
- Linux内核启动分析
张超<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 我的代码可见https://www.shiyanlo ...
- Linux内核jiffies简介
在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构struct timeval{time_t tv_sec; ...
- Linux内核中的jiffies及其作用介绍及jiffies等相关函数详解
在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构struct timeval{time_t tv_sec; ...
- mips64高精度时钟引起ktime_get时间不准,导致饿狗故障原因分析【转】
转自:http://blog.csdn.net/chenyu105/article/details/7720162 重点关注关中断的情况.临时做了一个版本,在CPU 0上监控所有非0 CPU的时钟中断 ...
随机推荐
- 红,X-Japan
ktv如果唱这首应该很爽,慢慢学. 红(くれない) 歌手:X JAPAN作词:YOSHIKI作曲:YOSHIKI I could not look back you'd gone away from ...
- ElasticSearch实战-日志监控平台
1.概述 在项目业务倍增的情况下,查询效率受到影响,这里我们经过讨论,引进了分布式搜索套件——ElasticSearch,通过分布式搜索来解决当下业务上存在的问题.下面给大家列出今天分析的目录: El ...
- Metasploit 笔记
目录一.名词解释···································································· 3二.msf基础··············· ...
- Sql Server 常用方法、存储过程备用
常用方法 --字符串转换成数字 --CAST("1" AS int) --CONVERT(int,"1") --截取字符串 SUBSTRING(OccurreA ...
- Physically Based Render in Game 序
基于物理渲的渲染理论,从SIGGRAPH06被Naty Hoffman等人提出后,近年来也越来越多的被各大游戏公司所采用,几乎已经是次世代游戏的标准特性,也是每个3D游戏工作者所必备的知识,尽管每年S ...
- PHP获取某远程网站的服务器时间
<?php function get_time($server){ $data = "HEAD / HTTP/1.1\r\n"; $data .= "Host: ...
- [SHELL实例] (转)最牛B的 Linux Shell 命令 (一)
本文编译自commandlinefu.com ( 应该是 Catonmat ) 的系列文章 Top Ten One-Liners from CommandLineFu Explained .作为一个由 ...
- CentOS上安装Node.js
CentOS上安装Node.js [日期:2014-07-21] 来源:Linux社区 作者:maskdfe [字体:大 中 小] CentOS上安装Node.js(想在Linux上学习No ...
- RT-Thread的线程间同步
禁止系统调度上一节< 多线程导致的临界区问题>中由于 test1 线程被 test2 线程打断,才导致了我们没有得到预期的结果,我们一般可通过关闭中断和调度器上锁这两种简单的途径来禁止系统 ...
- 不要让mysql犹豫不决
DROP TABLE IF EXISTS `w1`; CREATE TABLE `w1` ( `waid` ) NOT NULL AUTO_INCREMENT, `wa1` ) COLLATE utf ...