1. 程式人生 > >linux核心初始化步驟(十)-----時間管理子系統初始化

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;
}