您的位置:首页 > 运维架构 > Linux

linux时钟处理机制(一)

2014-02-22 13:27 387 查看

计算机系统中的计时器

在计算机系统中存在着许多硬件计时器,例如 Real Timer Clock ( RTC )、Time Stamp Counter ( TSC ) 和 Programmable Interval Timer ( PIT ) 等等。

Real Timer Clock ( RTC ):
独立于整个计算机系统(例如: CPU 和其他 chip )
内核利用其获取系统当前时间和日期

Time Stamp Counter ( TSC ):
从 Pentium 起,提供一个寄存器 TSC,用来累计每一次外部振荡器产生的时钟信号
通过指令 rdtsc 访问这个寄存器
比起 PIT,TSC 可以提供更精确的时间测量

Programmable Interval Timer ( PIT ):
时间测量设备
内核使用的产生时钟中断的设备,产生的时钟中断依赖于硬件的体系结构,慢的为 10 ms 一次,快的为 1 ms 一次

High Precision Event Timer ( HPET ):
PIT 和 RTC 的替代者,和之前的计时器相比,HPET 提供了更高的时钟频率(至少10 MHz )以及更宽的计数器宽度(64位)
一个 HPET 包括了一个固定频率的数值增加的计数器以及3到32个独立的计时器,这每一个计时器有包涵了一个比较器和一个寄存器(保存一个数值,表示触发中断的时机)。每一个比较器都比较计数器中的数值和寄存器中的数值,当这两个数值相等时,将产生一个中断

数据结构

和硬件计时器(本文又称作硬件时钟,区别于软件时钟)相关的数据结构主要有两个:

struct clocksource :对硬件设备的抽象,描述时钟源信息
/**
* struct cyclecounter - hardware abstraction for a free running counter
*	Provides completely state-free accessors to the underlying hardware.
*	Depending on which hardware it reads, the cycle counter may wrap
*	around quickly. Locking rules (if necessary) have to be defined
*	by the implementor and user of specific instances of this API.
*
* @read:		returns the current cycle value
* @mask:		bitmask for two's complement
*			subtraction of non 64 bit counters,
*			see CLOCKSOURCE_MASK() helper macro
* @mult:		cycle to nanosecond multiplier
* @shift:		cycle to nanosecond divisor (power of two)
*/
struct cyclecounter {
cycle_t (*read)(const struct cyclecounter *cc);
cycle_t mask;
u32 mult;
u32 shift;
};

/**
* struct timecounter - layer above a %struct cyclecounter which counts nanoseconds
*	Contains the state needed by timecounter_read() to detect
*	cycle counter wrap around. Initialize with
*	timecounter_init(). Also used to convert cycle counts into the
*	corresponding nanosecond counts with timecounter_cyc2time(). Users
*	of this code are responsible for initializing the underlying
*	cycle counter hardware, locking issues and reading the time
*	more often than the cycle counter wraps around. The nanosecond
*	counter will only wrap around after ~585 years.
*
* @cc:			the cycle counter used by this instance
* @cycle_last:		most recent cycle counter value seen by
*			timecounter_read()
* @nsec:		continuously increasing count
*/
struct timecounter {
const struct cyclecounter *cc;
cycle_t cycle_last;
u64 nsec;
};

/**
* cyclecounter_cyc2ns - converts cycle counter cycles to nanoseconds
* @cc:		Pointer to cycle counter.
* @cycles:	Cycles
*
* XXX - This could use some mult_lxl_ll() asm optimization. Same code
* as in cyc2ns, but with unsigned result.
*/
static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc,
cycle_t cycles)
{
u64 ret = (u64)cycles;
ret = (ret * cc->mult) >> cc->shift;
return ret;
}

/**
* timecounter_init - initialize a time counter
* @tc:			Pointer to time counter which is to be initialized/reset
* @cc:			A cycle counter, ready to be used.
* @start_tstamp:	Arbitrary initial time stamp.
*
* After this call the current cycle register (roughly) corresponds to
* the initial time stamp. Every call to timecounter_read() increments
* the time stamp counter by the number of elapsed nanoseconds.
*/
extern void timecounter_init(struct timecounter *tc,
const struct cyclecounter *cc,
u64 start_tstamp);

/**
* timecounter_read - return nanoseconds elapsed since timecounter_init()
*                    plus the initial time stamp
* @tc:          Pointer to time counter.
*
* In other words, keeps track of time since the same epoch as
* the function which generated the initial time stamp.
*/
extern u64 timecounter_read(struct timecounter *tc);

/**
* timecounter_cyc2time - convert a cycle counter to same
*                        time base as values returned by
*                        timecounter_read()
* @tc:		Pointer to time counter.
* @cycle_tstamp:	a value returned by tc->cc->read()
*
* Cycle counts that are converted correctly as long as they
* fall into the interval [-1/2 max cycle count, +1/2 max cycle count],
* with "max cycle count" == cs->mask+1.
*
* This allows conversion of cycle counter values which were generated
* in the past.
*/
extern u64 timecounter_cyc2time(struct timecounter *tc,
cycle_t cycle_tstamp);

/**
* struct clocksource - hardware abstraction for a free running counter
*	Provides mostly state-free accessors to the underlying hardware.
*	This is the structure used for system time.
*
* @name:		ptr to clocksource name
* @list:		list head for registration
* @rating:		rating value for selection (higher is better)
*			To avoid rating inflation the following
*			list should give you a guide as to how
*			to assign your clocksource a rating
*			1-99: Unfit for real use
*				Only available for bootup and testing purposes.
*			100-199: Base level usability.
*				Functional for real use, but not desired.
*			200-299: Good.
*				A correct and usable clocksource.
*			300-399: Desired.
*				A reasonably fast and accurate clocksource.
*			400-499: Perfect
*				The ideal clocksource. A must-use where
*				available.
* @read:		returns a cycle value, passes clocksource as argument
* @enable:		optional function to enable the clocksource
* @disable:		optional function to disable the clocksource
* @mask:		bitmask for two's complement
*			subtraction of non 64 bit counters
* @mult:		cycle to nanosecond multiplier
* @shift:		cycle to nanosecond divisor (power of two)
* @max_idle_ns:	max idle time permitted by the clocksource (nsecs)
* @maxadj:		maximum adjustment value to mult (~11%)
* @flags:		flags describing special properties
* @archdata:		arch-specific data
* @suspend:		suspend function for the clocksource, if necessary
* @resume:		resume function for the clocksource, if necessary
* @cycle_last:		most recent cycle counter value seen by ::read()
*/
struct clocksource {
/*
* Hotpath data, fits in a single cache line when the
* clocksource itself is cacheline aligned.
*/
cycle_t (*read)(struct clocksource *cs);
cycle_t cycle_last;
cycle_t mask;
u32 mult;
u32 shift;
u64 max_idle_ns;
u32 maxadj;
#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
struct arch_clocksource_data archdata;
#endif

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

/* private: */
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
/* Watchdog related data, used by the framework */
struct list_head wd_list;
cycle_t cs_last;
cycle_t wd_last;
#endif
} ____cacheline_aligned;


struct clock_event_device :时钟的事件信息,包括当硬件时钟中断发生时要执行那些操作(实际上保存了相应函数的指针)。本文将该结构称作为“时钟事件设备”。
/**
* struct clock_event_device - clock event device descriptor
* @event_handler:	Assigned by the framework to be called by the low
*			level handler of the event source
* @set_next_event:	set next event function using a clocksource delta
* @set_next_ktime:	set next event function using a direct ktime value
* @next_event:		local storage for the next event in oneshot mode
* @max_delta_ns:	maximum delta value in ns
* @min_delta_ns:	minimum delta value in ns
* @mult:		nanosecond to cycles multiplier
* @shift:		nanoseconds to cycles divisor (power of two)
* @mode:		operating mode assigned by the management code
* @features:		features
* @retries:		number of forced programming retries
* @set_mode:		set mode function
* @broadcast:		function to broadcast events
* @min_delta_ticks:	minimum delta value in ticks stored for reconfiguration
* @max_delta_ticks:	maximum delta value in ticks stored for reconfiguration
* @name:		ptr to clock event name
* @rating:		variable to rate clock event devices
* @irq:		IRQ number (only for non CPU local devices)
* @cpumask:		cpumask to indicate for which CPUs this device works
* @list:		list head for the management code
*/
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_mode	mode;
unsigned int		features;
unsigned long		retries;

void			(*broadcast)(const struct cpumask *mask);
void			(*set_mode)(enum clock_event_mode mode,
struct clock_event_device *);
unsigned long		min_delta_ticks;
unsigned long		max_delta_ticks;

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


需要特别注意的是结构 clock_event_device 的成员 event_handler ,它指定了当硬件时钟中断发生时,内核应该执行那些操作,也就是真正的时钟中断处理函数。 在
Linux 内核维护了两个链表,分别存储了系统中所有时钟源的信息和时钟事件设备的信息。这两个链表的表头在内核中分别是 clocksource_list 和 clockevent_devices 。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: