linux时钟处理机制(一)

计算机系统中的计时器

在计算机系统中存在着许多硬件计时器,例如 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 。

linux时钟处理机制(一)_第1张图片

你可能感兴趣的:(Linux内核)