参考博文:https://blog.csdn.net/DroidPhone/article/details/8051405
时间管理子系统:
/* 内核用jiffies变量记录系统启动以来经过的时钟滴答数*/
Jiffies.c (kernel\time):core_initcall(init_jiffies_clocksource)
static int __init init_jiffies_clocksource(void)
{
return clocksource_register(&clocksource_jiffies);
}
struct clocksource clocksource_jiffies = {
.name = "jiffies",
.rating = 1, /* lowest valid rating*/
.read = jiffies_read,
.mask = 0xffffffff, /*32bits*/
.mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
.shift = JIFFIES_SHIFT,
};
/*它的精度只有1/HZ秒,rating值为1*/
/*如果平台的代码没有提供定制的clocksource_default_clock函数,它将返回上面的clocksource*/
struct clocksource * __init __weak clocksource_default_clock(void)
{
return &clocksource_jiffies;
}
Clocksource.c (kernel\time):fs_initcall(clocksource_done_booting);
/*
* clocksource_done_booting - Called near the end of core bootup
*
* Hack to avoid lots of clocksource churn at boot time.
* We use fs_initcall because we want this to start before
* device_initcall but after subsys_initcall.
*/
static int __init clocksource_done_booting(void)
{
mutex_lock(&clocksource_mutex);
curr_clocksource = clocksource_default_clock();
mutex_unlock(&clocksource_mutex);
finished_booting = 1;
/*
* Run the watchdog first to eliminate unstable clock sources
*/
clocksource_watchdog_kthread(NULL);
mutex_lock(&clocksource_mutex);
/*经过该函数后,curr_clocksource将会被设为最合适的clocksource。
如果clocksource_select函数认为需要切换更好的时钟源,它会通过
timekeeping_notify通知timekeeping系统,使用新的clocksource进行
时间技术和更新操作*/
clocksource_select();
mutex_unlock(&clocksource_mutex);
return 0;
}
/ *内核管理着多种时间,它们分别是:
RTC时间
wall time:墙上时间
monotonic time
raw monotonic time
boot time:总启动时间
RTC时间:RTC一般有专门的电池来供电,软件可以读取该硬件来获得时间信息。
xtime:xtime相比于只能达到毫秒级别的RTC时间,它的精度取决于用于对xtime记时的clocksource,甚至可以达到纳秒级别,因为xtime实际是内存中的一个变量,访问速度非常快,记录自1970年1月1日24时到当前时刻所经历的纳秒数。
monotonic time:该时间自系统开机后一直单调递增,与xtime可以因用户的调整时间而产生跳变不同,该时间不计算系统休眠的时间。
raw monotonic time:更纯净,与monotonic time 相比,它不会受到NTP时间调整的影响。
Timekeeping.c (kernel\time):device_initcall(timekeeping_init_ops);
/*
* timekeeping_init - Initializes the clocksource and common timekeeping values
*/
void __init timekeeping_init(void)
{
struct clocksource *clock;
unsigned long flags;
struct timespec now, boot;
/*首先从RTC中获取当前时间*/
read_persistent_clock(&now);//获取RTC硬件时间
read_boot_clock(&boot);//获取启动的时间
/*对锁和ntp进行必要的初始化*/
write_seqlock_irqsave(&xtime_lock, flags);
ntp_init();
/*获取默认的clocksource,如果平台没有重新实现clocksourc_default_clock
函数,默认的clocksource就是基于jiffies的clocksource_jiffies,然后通过timekeeper_setup_internals(clock)函数把timekeeper和clocksource进行关联*/
clock = clocksource_default_clock();
if (clock->enable)
clock->enable(clock);
timekeeper_setup_internals(clock);
/*利用RTC的当前时间,初始化xtime,raw_time,wall_to_motonic等
字段,xtime字段因为是保存在内存中,系统掉电后无法保存时间信息
,所以每次启动都要通过timekeeping_init从RTC中同步正确的时间信
息*/
xtime.tv_sec = now.tv_sec;
xtime.tv_nsec = now.tv_nsec;
raw_time.tv_sec = 0;
raw_time.tv_nsec = 0;
if (boot.tv_sec == 0 && boot.tv_nsec == 0) {
boot.tv_sec = xtime.tv_sec;
boot.tv_nsec = xtime.tv_nsec;
}
set_normalized_timespec(&wall_to_monotonic,
-boot.tv_sec, -boot.tv_nsec);
/*total_sleep_time字段初始化为0*/
total_sleep_time.tv_sec = 0;
total_sleep_time.tv_nsec = 0;
write_sequnlock_irqrestore(&xtime_lock, flags);
}
组织与时间相关的timekeeper结构
/* Structure holding internal timekeeping values. */
struct timekeeper {
/* Current clocksource used for timekeeping. */
struct clocksource *clock;
/* The shift value of the current clocksource. */
int shift;
/* Number of clock cycles in one NTP interval. */
cycle_t cycle_interval;
/* Number of clock shifted nano seconds in one NTP interval. */
u64 xtime_interval;
/* shifted nano seconds left over when rounding cycle_interval */
s64 xtime_remainder;
/* Raw nano seconds accumulated per NTP interval. */
u32 raw_interval;
/* Clock shifted nano seconds remainder not stored in xtime.tv_nsec. */
u64 xtime_nsec;
/* Difference between accumulated time and NTP time in ntp
* shifted nano seconds. */
s64 ntp_error;
/* Shift conversion between clock shifted nano seconds and
* ntp shifted nano seconds. */
int ntp_error_shift;
/* NTP adjusted clock multiplier */
u32 mult;
};
上面是对时钟源设备的设置,接下来是时钟事件设备:clock_event_device。它们的区别在于,clocksource不能被编程,没有产生事件的能力,它主要被用于timekeeper来实现对真实时间进行精确统记,而clock_event_device是可编程的,它可以工作在周期触发或单词触发模式,系统可以对它进行编程,以确定下一次事件触发的时间,clock_event_device主要用于实现普通定时器和高精度定时器,同时也用于产生tick事件,供给进程调度子系统使用。时钟事件设备与通用事件框架中其他模块的关系如下图所示:
下面让我们来依次分析框架层和machine级别的初始化和注册
这里需要与之前分析的Linux内核初始化步骤(一)等文章中的start_kernel函数分析结合起来,从start_kernel开始,调用tick_init,它位于kernel/time/tick-common.c中,调用clockevents_register_notifier,同时把类型为notifier_block的tick_notifier作为参数传入,用于注册一个通知链,这样,当系统中clock_event_device状态发生变化时(新增,删除,挂起,唤醒等等),tick_notifier中的notifier_call字段中设定的回调函数tick_notify就会被调用。
/**
* tick_init - initialize the tick control
*
* Register the notifier with the clockevents framework
*/
void __init tick_init(void)
{
clockevents_register_notifier(&tick_notifier);
}
接下来start_kernel调用了time_init函数
void __init time_init(void)
{
system_timer = machine_desc->timer;
system_timer->init();
#ifdef CONFIG_HAVE_SCHED_CLOCK
sched_clock_postinit();
#endif
}
mchine_desc->timer指向已经注册到机器描述符里的davinci_timer结构体,它的实现如下:
setup_arch()–>
mdesc = setup_machine_tags(machine_arch_type)–>
machine_desc = mdesc,最终会匹配到与开发板型号( DaVinci DA850/OMAP-L138/AM18x EVM)相对应的机器描述符(struct machine_desc)。
system_timer是一个全局变量:
system_timer = machine_desc->timer;
这个变量最终指向的是arch/arm/mach-davinci/time.c中的
struct sys_timer davinci_timer = {
.init = davinci_timer_init,
};
davinci_timer_init会进行与平台相关的初始化:
static void __init davinci_timer_init(void)
{
struct clk *timer_clk;
struct davinci_soc_info *soc_info = &davinci_soc_info;
unsigned int clockevent_id;
unsigned int clocksource_id;
static char err[] __initdata = KERN_ERR
"%s: can't register clocksource!\n";
int i;
clockevent_id = soc_info->timer_info->clockevent_id;
clocksource_id = soc_info->timer_info->clocksource_id;
timers[TID_CLOCKEVENT].id = clockevent_id;
timers[TID_CLOCKSOURCE].id = clocksource_id;
/*
* 如果clock events & clocksource,使用同一个定时器,必须用比较寄存器来产生
* 事件中断,这等价于仅有一次的定时器(不是周期性的)
*/
if (clockevent_id == clocksource_id) {
struct davinci_timer_instance *dtip =
soc_info->timer_info->timers;
int event_timer = ID_TO_TIMER(clockevent_id);
/* Only bottom timers can use compare regs */
if (IS_TIMER_TOP(clockevent_id))
pr_warning("davinci_timer_init: Invalid use"
" of system timers. Results unpredictable.\n");
else if ((dtip[event_timer].cmp_off == 0)
|| (dtip[event_timer].cmp_irq == 0))
pr_warning("davinci_timer_init: Invalid timer instance"
" setup. Results unpredictable.\n");
else {
timers[TID_CLOCKEVENT].opts |= TIMER_OPTS_USE_COMPARE;
clockevent_davinci.features = CLOCK_EVT_FEAT_ONESHOT;
}
}
timer_clk = clk_get(NULL, "timer0");
BUG_ON(IS_ERR(timer_clk));
clk_enable(timer_clk);
/* 初始化定时器的硬件*/
timer_init();
davinci_clock_tick_rate = clk_get_rate(timer_clk);
/* setup clocksource */
clocksource_davinci.read = read_cycles;
clocksource_davinci.name = id_to_name[clocksource_id];
if (clocksource_register_hz(&clocksource_davinci,
davinci_clock_tick_rate))
printk(err, clocksource_davinci.name);
/* setup clockevent */
clockevent_davinci.name = id_to_name[timers[TID_CLOCKEVENT].id];
clockevent_davinci.mult = div_sc(davinci_clock_tick_rate, NSEC_PER_SEC,
clockevent_davinci.shift);
clockevent_davinci.max_delta_ns =
clockevent_delta2ns(0xfffffffe, &clockevent_davinci);
clockevent_davinci.min_delta_ns = 50000; /* 50 usec */
clockevent_davinci.cpumask = cpumask_of(0);
clockevents_register_device(&clockevent_davinci);
for (i=0; i< ARRAY_SIZE(timers); i++)
timer32_config(&timers[i]);
}
定时器硬件部分的初始化
static void __init timer_init(void)
{
struct davinci_soc_info *soc_info = &davinci_soc_info;
struct davinci_timer_instance *dtip = soc_info->timer_info->timers;
void __iomem *base[2];
int i;
/* 每一个64位定时器作为整体的全局初始化 */
for(i=0; i<2; i++) {
u32 tgcr;
base[i] = ioremap(dtip[i].base, SZ_4K);
if (WARN_ON(!base[i]))
continue;
/* 禁止内部的中断源 */
__raw_writel(0, base[i] + TCR);
/* 重启两个定时器, timer34禁止预分频 */
tgcr = 0;
__raw_writel(tgcr, base[i] + TGCR);
/* 把两个定时器都设置为 unchained 32-bit模式 */
tgcr = TGCR_TIMMODE_32BIT_UNCHAINED << TGCR_TIMMODE_SHIFT;
__raw_writel(tgcr, base[i] + TGCR);
/* Unreset timers */
tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) |
(TGCR_UNRESET << TGCR_TIM34RS_SHIFT);
__raw_writel(tgcr, base[i] + TGCR);
/* 把两个计数器都初始化为0 */
__raw_writel(0, base[i] + TIM12);
__raw_writel(0, base[i] + TIM34);
}
/* 把每个定时器都初始化为 32-bit timer */
for (i=0; i< ARRAY_SIZE(timers); i++) {
struct timer_s *t = &timers[i];
int timer = ID_TO_TIMER(t->id);
u32 irq;
t->base = base[timer];
if (!t->base)
continue;
if (IS_TIMER_BOT(t->id)) {
t->enamode_shift = 6;
t->tim_off = TIM12;
t->prd_off = PRD12;
irq = dtip[timer].bottom_irq;
} else {
t->enamode_shift = 22;
t->tim_off = TIM34;
t->prd_off = PRD34;
irq = dtip[timer].top_irq;
}
/*注册中断 */
t->irqaction.name = t->name;
t->irqaction.dev_id = (void *)t;
if (t->irqaction.handler != NULL) {
irq = USING_COMPARE(t) ? dtip[i].cmp_irq : irq;
setup_irq(irq, &t->irqaction);
}
}
}
定时器软件中断的实现(低分辨率定时器的原理和实现)
系统初始化过程中,start_kernel会调用定时器系统的初始化函数init_timers:
void __init init_timers(void)
{
int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
init_timer_stats();
BUG_ON(err != NOTIFY_OK);
register_cpu_notifier(&timers_nb);//注册cpu notify,以便在hotplug时在cpu之间进行定时器的迁移
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}
利用定时器,我们可以设定在未来的某一时刻,触发一个特定的事件。所谓低分辨率定时器,是指这种定时器的计时单位基于jiffies值的技术,也就是说,它的精度只有1/HZ,假如内核配置的HZ是1000,那意味着系统中的低分辨率定时器的精度是1ms。
后来又出现了高分辨率定时器,它可以提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动。
void __init hrtimers_init(void)
{
hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
register_cpu_notifier(&hrtimers_nb);
#ifdef CONFIG_HIGH_RES_TIMERS
open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
#endif
}
Posix-timers.c (kernel):__initcall(init_posix_timers);
/*
* Initialize everything, well, just everything in Posix clocks/timers ;)
*/
static __init int init_posix_timers(void)
{
struct k_clock clock_realtime = {
.clock_getres = hrtimer_get_res,
.clock_get = posix_clock_realtime_get,
.clock_set = posix_clock_realtime_set,
.clock_adj = posix_clock_realtime_adj,
.nsleep = common_nsleep,
.nsleep_restart = hrtimer_nanosleep_restart,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
.timer_get = common_timer_get,
.timer_del = common_timer_del,
};
struct k_clock clock_monotonic = {
.clock_getres = hrtimer_get_res,
.clock_get = posix_ktime_get_ts,
.nsleep = common_nsleep,
.nsleep_restart = hrtimer_nanosleep_restart,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
.timer_get = common_timer_get,
.timer_del = common_timer_del,
};
struct k_clock clock_monotonic_raw = {
.clock_getres = hrtimer_get_res,
.clock_get = posix_get_monotonic_raw,
};
struct k_clock clock_realtime_coarse = {
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_realtime_coarse,
};
struct k_clock clock_monotonic_coarse = {
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_monotonic_coarse,
};
struct k_clock clock_boottime = {
.clock_getres = hrtimer_get_res,
.clock_get = posix_get_boottime,
.nsleep = common_nsleep,
.nsleep_restart = hrtimer_nanosleep_restart,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
.timer_get = common_timer_get,
.timer_del = common_timer_del,
};
posix_timers_register_clock(CLOCK_REALTIME, &clock_realtime);
posix_timers_register_clock(CLOCK_MONOTONIC, &clock_monotonic);
posix_timers_register_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);
posix_timers_register_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse);
posix_timers_register_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse);
posix_timers_register_clock(CLOCK_BOOTTIME, &clock_boottime);
posix_timers_cache = kmem_cache_create("posix_timers_cache",
sizeof (struct k_itimer), 0, SLAB_PANIC,
NULL);
idr_init(&posix_timers_id);
return 0;
}
Posix-cpu-timers.c (kernel):__initcall(init_posix_cpu_timers);
static __init int init_posix_cpu_timers(void)
{
struct k_clock process = {
.clock_getres = process_cpu_clock_getres,
.clock_get = process_cpu_clock_get,
.timer_create = process_cpu_timer_create,
.nsleep = process_cpu_nsleep,
.nsleep_restart = process_cpu_nsleep_restart,
};
struct k_clock thread = {
.clock_getres = thread_cpu_clock_getres,
.clock_get = thread_cpu_clock_get,
.timer_create = thread_cpu_timer_create,
};
struct timespec ts;
posix_timers_register_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
posix_timers_register_clock(CLOCK_THREAD_CPUTIME_ID, &thread);
cputime_to_timespec(cputime_one_jiffy, &ts);
onecputick = ts.tv_nsec;
WARN_ON(ts.tv_sec != 0);
return 0;
}
/* 初始化clock source的sys file system接口,linux 内核提供了pseudo-bus这个虚拟总线,这条总线主要用于cpu,中断控制器,timer等系统设备。sysdev_class,sysdev_device和sysdev_driver组成系统设备三件套,对应设备模型的bus type,device和driver。system device 模型中需要处理的也是和Linux设备模型中类似的逻辑:注册设备,注册driver和设备的匹配等 */
Clocksource.c (kernel\time):device_initcall(init_clocksource_sysfs);
static int __init init_clocksource_sysfs(void)
{
int error = subsys_system_register(&clocksource_subsys, NULL);
if (!error)
error = device_register(&device_clocksource);
if (!error)
error = device_create_file(
&device_clocksource,
&dev_attr_current_clocksource);
if (!error)
error = device_create_file(
&device_clocksource,
&dev_attr_available_clocksource);
return error;
}
Timer_list.c (kernel\time):__initcall(init_timer_list_procfs);
static int __init init_timer_list_procfs(void)
{
struct proc_dir_entry *pe;
pe = proc_create("timer_list", 0444, NULL, &timer_list_fops);
if (!pe)
return -ENOMEM;
return 0;
}
/* 初始化定时器结构*/
Alarmtimer.c (kernel\time):device_initcall(alarmtimer_init);
/**
* alarm_init - Initialize an alarm structure
* @alarm: ptr to alarm to be initialized
* @type: the type of the alarm
* @function: callback that is run when the alarm fires
*/
void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
{
timerqueue_init(&alarm->node);
alarm->function = function;
alarm->type = type;
alarm->state = ALARMTIMER_STATE_INACTIVE;
}