《Linux内核设计与实现》读书笔记(十一)

系统中有很多与时间相关的程序(比如定期执行的任务,服务器空间,某一时间执行的任务,推迟一段时间执行的任务),因此,时间的管理对于linux来说非常重要。

主要内容:

1. 系统时间

系统中管理的时间有2种:实际时间和定时器。

1.1 实际时间

实际时间就是现实中钟表上显示的时间,其实内核中并不常用这个时间,主要是用户空间的程序有时需要获取当前时间,

所以内核中也管理着这个时间。

实际时间的获取是在开机后,内核初始化时从RTC读取的。

内核读取这个时间后就将其放入内核中的 xtime 变量中,并且在系统的运行中不断更新这个值。

注:RTC就是实时时钟的缩写,它是用来存放系统时间的设备。一般和BIOS一样,由主板上的电池供电的,所以即使关机也可将时间保存。

实际时间存放的变量 xtime 在文件 kernel/time/timekeeping.c中。

timespec xtime __attribute__ ((aligned (16))); timespec {__kernel_time_t tv_sec;tv_nsec;};typedef long__kernel_time_t;

系统读写 xtime 时用的就是顺序锁。

do_settimeofday(struct timespec *tv){write_seqlock_irqsave(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags); ;}do_gettimeofday(struct timeval *tv){struct timespec now;getnstimeofday(&now); tv->tv_sec = now.tv_sec;tv->tv_usec = now.tv_nsec/1000;}void getnstimeofday(struct timespec *ts){ {seq = read_seqbegin(&xtime_lock);*ts = xtime;nsecs = timekeeping_get_ns();nsecs += arch_gettimeoffset();} while (read_seqretry(&xtime_lock, seq));}

上述场景中,写锁必须要优先于读锁(因为 xtime 必须及时更新),而且写锁的使用者很少(一般只有系统定期更新xtime的线程需要持有这个锁)。

这正是 顺序锁的应用场景。

1.2 定时器

定时器是内核中主要使用的时间管理方法,通过定时器,可以有效的调度程序的执行。

动态定时器是内核中使用比较多的定时器,下面重点讨论的也是动态定时器。

2. 定时器

内核中的定时器有2种,静态定时器和动态定时器。

静态定时器一般执行了一些周期性的固定工作:

动态定时器顾名思义,是在需要时(一般是推迟程序执行)动态创建的定时器,使用后销毁(一般都是只用一次)。

一般我们在内核代码中使用的定时器基本都是动态定时器,下面重点讨论动态定时器相关的概念和使用方法。

3. 定时器相关概念

定时器的使用中,下面3个概念非常重要:

3.1 HZ

节拍率(HZ)是时钟中断的频率,表示的一秒内时钟中断的次数。

比如 HZ=100 表示一秒内触发100次时钟中断程序。

HZ的值一般与体系结构有关,x86 体系结构一般定义为 100,参考文件 include/asm-generic/param.h

HZ值的大小的设置过程其实就是平衡 精度和性能 的过程,并不是HZ值越高越好。

HZ值

优势

劣势

高HZ时钟中断程序运行的更加频繁,依赖时间执行的程序更加精确, 对资源消耗和系统运行时间的统计更加精确。 时钟中断执行的频繁,增加系统负担时钟中断占用的CPU时间过多

此外,有一点需要注意,内核中使用的HZ可能和用户空间中定义的HZ值不一致,为了避免用户空间取得错误的时间,

内核中也定义了 USER_HZ,即用户空间使用的HZ值。

一般来说,USER_HZ 和 HZ 都是相差整数倍,内核中通过函数 jiffies_to_clock_t 来将内核来将内核中的 jiffies转为 用户空间 jiffies

/* 参见文件: kernel/time.c *//* * Convert jiffies/jiffies_64 to clock_t and back. */clock_t jiffies_to_clock_t(unsigned long x){#if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0# if HZ < USER_HZreturn x * (USER_HZ / HZ);# elsereturn x / (HZ / USER_HZ);# endif#elsereturn div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ);#endif}EXPORT_SYMBOL(jiffies_to_clock_t);

3.2 jiffies

jiffies用来记录自系统启动以来产生的总节拍数。比如系统启动了 N 秒,那么 jiffies就为 N×HZ

jiffies的相关定义参考头文件 <linux/jiffies.h> include/linux/jiffies.h

u64 __jiffy_data jiffies_64;extern unsigned long volatile __jiffy_data jiffies;

使用定时器时一般都是以jiffies为单位来延迟程序执行的,比如延迟5个节拍后执行的话,执行时间就是 jiffies+5

32位的jiffies的最大值为 2^32-1,在使用时有可能会出现回绕的问题。

比如下面的代码:

unsigned (timeout < jiffies){}

正常情况下,上面的代码没有问题。当jiffies接近最大值的时候,就会出现回绕问题。

由于是unsinged long类型,所以jiffies达到最大值后会变成0然后再逐渐变大,如下图所示:

所以在上述的循环代码中,会出现如下情况:

为了回避回扰的问题,可以使用<linux/jiffies.h>头文件中提供的 time_after,time_before等宏

游手好闲会使人心智生锈

《Linux内核设计与实现》读书笔记(十一)

相关文章:

你感兴趣的文章:

标签云: