栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

【Linux时钟系统】

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Linux时钟系统】

Linux系统时间
  • 时钟系统,频率的产生
    • RTC时钟系统
    • Linux时钟中断
  • struct clocksource
  • struct timekeeper
  • struct clock_event_device
  • struct tick_device
  • 链接

时钟系统,频率的产生 RTC时钟系统
1. cpu的时间产生不一定如此,用来阐述硬件产生滴答的原理
2. 在断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走;可以产生频率;
3. 通过振荡器作为时钟源被用来驱动系统时钟;2种二级时钟源,40KHz低速内部RC,32.768KHz低速外部晶体;
Linux时钟中断
1. Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断;
2. 操作系统对可编程定时/计数器进行有关初始化,然后定时/计数器就对输入脉冲进行计数(分频),产生的三个输出脉冲Out0、Out1、Out2各有用途,很多接口书都介绍了这个问题,我们只看Out0上的输出脉冲,这个脉冲信号接到中断控制器8259A_1的0号管脚,触发一个周期性的中断,我们就把这个中断叫做时钟中断,时钟中断的周期,也就是脉冲信号的周期,我们叫做“滴答”或“时标”(tick);
3. 


struct clocksource
struct clocksource {
	u64 (*read)(struct clocksource *cs);
	u64 mask;
	u32 mult;
	u32 shift;
	u64 max_idle_ns;
	u32 maxadj;
#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
	struct arch_clocksource_data archdata;
#endif
	u64 max_cycles;
	const char *name;
	struct list_head list;
	int rating;
	int (*enable)(struct clocksource *cs);
	void (*disable)(struct clocksource *cs);
	unsigned long flags;
	void (*suspend)(struct clocksource *cs);
	void (*resume)(struct clocksource *cs);
	void (*mark_unstable)(struct clocksource *cs);
	void (*tick_stable)(struct clocksource *cs);

	
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
	
	struct list_head wd_list;
	u64 cs_last;
	u64 wd_last;
#endif
	struct module *owner;
};
1. 该结构体是对真实的时钟源的进行的软件抽象;
2. 该抽象的时钟源本身不会产生中断,要获得当前时钟源的计数,需要主动调用read回调函数获得当前的计数值,即cycle,然后借助成员变量mult和shift来完成计算;
3. 内核的启动阶段,会注册一个基于jiffies的clocksource ,然后注册到系统中,注意,注册时候,成员变量还没有赋值;
4. 时钟发生器是硬件设备,通过时钟发生器产生的循环,可以读到cycle信息,进而获得时间;(对时间产生的理解)
struct timekeeper
struct timekeeper {
	struct tk_read_base	tkr_mono;
	struct tk_read_base	tkr_raw;
	u64			xtime_sec;
	unsigned long		ktime_sec;
	struct timespec64	wall_to_monotonic;
	ktime_t			offs_real;
	ktime_t			offs_boot;
	ktime_t			offs_tai;
	s32			tai_offset;
	unsigned int		clock_was_set_seq;
	u8			cs_was_changed_seq;
	ktime_t			next_leap_ktime;
	u64			raw_sec;

	
	u64			cycle_interval;
	u64			xtime_interval;
	s64			xtime_remainder;
	u64			raw_interval;
	
	u64			ntp_tick;
	
	s64			ntp_error;
	u32			ntp_error_shift;
	u32			ntp_err_mult;
	
	u32			skip_second_overflow;
#ifdef CONFIG_DEBUG_TIMEKEEPING
	long			last_warning;
	
	int			underflow_seen;
	int			overflow_seen;
#endif
};
1. 时间的分类
	- RTC时间:通过专门的硬件实现;毫秒级别,如果位于外部的RTC芯片,访问速度也比较慢;
	- xtime:墙上时间,内存中的变量,精度较高,可以达到纳秒级别;CLOCK_REALTIME
	- monotonic time:自系统开机就一直增加,不计算系统的休眠的时间;CLOCK_MONOTONIC
	- raw monotonic time:代表独立时钟硬件对时间的统计;CLOCK_MONOTONIC_RAW
2. 上述时间是依赖于结构成员clock指向的时钟源的;更新clocksource会通过通知链通知timekeeper作时钟源的改变;
3. 默认的clocksource是基于jiffies的clocksource_jiffies;利用rtc的当前时间,对timekeeper中的成员变量xtime,raw_time,wall_to_monotonic等字段进行描述;然后初始化代表实际时间与monotonic时间之间的偏移量的offs_read字段,total_sleep_time字段初始化为0;
4. xtime字段因为是保存在内存中,系统掉电后无法保存时间信息,所以每次启动时都要通过timekeeping_init从RTC中同步正确的时间信息;
5. xtime同步完RTC的数据之后,timekeeper就会独立于rtc,利用自身关联的clocksource进行时间的更新;每次xtime更新会调用一次do_timer,增加jiffies计数;
6. 根据内核的配置项的不同,更新时间的操作发生的频度也不尽相同,如果没有配置NO_HZ选项,通常每个tick的定时中断周期,do_timer会被调用一次,相反,如果配置了NO_HZ选项,可能会在好几个tick后,do_timer才会被调用一次;




struct clock_event_device
struct clock_event_device {
	void			(*event_handler)(struct clock_event_device *);
	int			(*set_next_event)(unsigned long evt, struct clock_event_device *);
	int			(*set_next_ktime)(ktime_t expires, struct clock_event_device *);
	ktime_t			next_event;
	u64			max_delta_ns;
	u64			min_delta_ns;
	u32			mult;
	u32			shift;
	enum clock_event_state	state_use_accessors;
	unsigned int		features;
	unsigned long		retries;

	int			(*set_state_periodic)(struct clock_event_device *);
	int			(*set_state_oneshot)(struct clock_event_device *);
	int			(*set_state_oneshot_stopped)(struct clock_event_device *);
	int			(*set_state_shutdown)(struct clock_event_device *);
	int			(*tick_resume)(struct clock_event_device *);

	void			(*broadcast)(const struct cpumask *mask);
	void			(*suspend)(struct clock_event_device *);
	void			(*resume)(struct clock_event_device *);
	unsigned long		min_delta_ticks;
	unsigned long		max_delta_ticks;

	const char		*name;
	int			rating;
	int			irq;
	int			bound_on;
	const struct cpumask	*cpumask;
	struct list_head	list;
	struct module		*owner;
} ____cacheline_aligned;

struct sys_timer {
	void			(*init)(void);
	void			(*suspend)(void);
	void			(*resume)(void);
#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
	unsigned long		(*offset)(void);
#endif
}
1. clock_event_device与clocksource的差别:clocksource不能被编程,没有产生事件的能力,它主要被用于timekeeper来实现对真实时间进行精确的统计;而clock_event_device则是可编程的,它可以工作在周期触发或单次触发模式,系统可以对它进行编程,以确定下一次事件触发的时间,clock_event_device主要用于实现普通定时器和高精度定时器,同时也用于产生tick事件,供给进程调度子系统使用;
2. tick_device是基于clock_event_device的进一步封装,用于代替原有的时钟滴答中断,给内核提供tick事件,以完成进程的调度和进程信息统计,负载平衡和时间更新等操作;
3. 时钟事件设备的上层抽象如图,与硬件相关的代码进行了分离;
4. 时钟事件设备的核心数据结构是clock_event_device结构,它代表着一个时钟硬件设备,该设备就好像是一个具有事件触发能力(通常就是指中断)的clocksource,它不停地计数,当计数值达到预先编程设定的数值那一刻,会引发一个时钟事件中断,继而触发该设备的事件处理回调函数,以完成对时钟事件的处理;
5. 每一个machine,都要定义一个自己的machine_desc结构,该结构定义了该machine的一些最基本的特性,其中需要设定一个sys_timer结构指针,machine级的代码负责定义sys_timer结构;该init回调函数的主要作用就是完成系统中的clocksource和clock_event_device的硬件初始化工作;同时注册通知链;


struct tick_device
struct tick_device {
	struct clock_event_device *evtdev;
	enum tick_device_mode mode;
};
1. 当内核没有配置成支持高精度定时器时,系统的tick由tick_device产生,tick_device其实是clock_event_device的简单封装,它内嵌了一个clock_event_device指针和它的工作模式;
2. DEFINE_PER_CPU(struct tick_device, tick_cpu_device);
3. 在更新clock_event_deviec时,通知链会调用到tick_check_new_device函数判断是否可以用于本cpu;
4. TICKDEV_MODE_PERIODIC或TICKDEV_MODE_ONESHOT模式;oneshot模式才会支持NO_HZ和HRTIMER,其根据tick_device的特性设置;
5. tick事件的处理:tick_checkouk_new_device之后;
6. 在do_timer函数中,完成一下操作;更新jiffies的时间,更新墙上时间,每10个tick,更新一次cpu的负载信息;通过调用update_process_times完成以下工作:1).更新进程的时间统计信息 2).触发TIMER_SOFTIRQ软件中断,以便系统处理传统的低分辨率定时器;3).检查rcu的callback;4). 通过scheduler_tick触发调度系统进行进程统计和调度工作;

链接

RTC实时时钟,步骤超细详解,一文看懂RTC
Linux时间子系统之一:clock source(时钟源)
Linux之时钟中断

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/826061.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号