linux核心初始化步驟(十)-----時間管理子系統初始化
參考博文: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;
}