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

猜你喜欢

转载自blog.csdn.net/qq_40788950/article/details/84636621