Linux时间子系统的软件架构(一)

一.软件架构
但是随着技术发展,出现了下面两种新的需求:
(1)嵌入式设备需要较好的电源管理策略。传统的linux会有一个周期性的时钟,即便是系统无事可做的时候也要醒来,这样导致系统不断的从低功耗(idle)状态进入高功耗的状态。这样的设计不符合电源管理的需求。
(2)多媒体的应用程序需要非常精确的timer,例如为了避免视频的跳帧、音频回放中的跳动,这些需要系统提供足够精度的timer
和低精度timer不同,高精度timer使用了人类的最直观的时间单位ns(低精度timer使用的tick是和内核配置相关,不够直接)。本质上linux kernel提供了高精度timer之后,其实不必提供低精度timer了,不过由于低精度timer存在了很长的历史,并且在渗入到内核各个部分,如果去掉低精度timer很容易引起linux kernel稳定性和健壮性的问题,因此目前的linux kernel保持了低精度timer和高精度timer并存。
在新的需求的推动下,内核开发者对linux的时间子系统的软件框架进行修改,让代码层次更清晰,同时又是灵活可配置的,一个示意性的block图如下所示:
这里写图片描述
在引入multi-core之后,过去HW timer的功能被分成两个部分,一个是free running的system counter,是全局的,不属于任何一个CPU。另外一部分就是产生定时事件的HW block,我们称之timer,timer硬件被嵌入到各个cpu core中,因此,我们更准确的称之为CPU local Timer,这些timer都是基于一个Global counter运作的。在驱动层,我们提供一个clock source chip driver的模块来驱动硬件,这是模块是和硬件体系结构有关的。如果系统内存在多个HW timer和counter block,那么系统中可能会存在多个clock source chip driver。
面对形形色色的timer和counter硬件,linux kernel抽象出了通用clock event layer和通用clock source模块,这两个模块和硬件无关。底层的clock source chip driver会调用通用clock event和clock source模块的接口函数,注册clock source和clock event设备。clock source设备对应硬件的system free running counter,提供了一个基础的timeline。当然了,实际的timeline象一条直线,无限延伸。对于kernel而言,其timeline是构建在system free running counter之上的,因此clocksource 对应的timeline存在溢出问题。如果选用64bit的HW counter,并且输入频率不那么高,那么溢出时间可能会长达50年甚至更多,那么从应用的角度来看,能维持50年左右的timeline也可以接受。如果说clock source是一个time line,那么clock event是在timeline上指定的点产生clock event的设备,之所以能产生异步事件,当然是基于中断子系统了,clock source chip driver会申请中断并调用通用clock event模块的callback函数来通知这样的异步事件。
tick device layer基于clock event设备进行工作的:一般而言,每个CPU形成自己的一个小系统,有自己的调度、有自己的进程统计等,这个小系统都是拥有自己的tick设备,而且是唯一的。对于clock event设备而言就不是这样了,硬件有多少个timer硬件就注册多少个clock event device,各个cpu的tick device会选择自己适合的那个clock event设备。tick device可以工作在periodic mode或者one shot mode,当然,这是和系统配置有关。因此,在tick device layer,有多少个cpu,就会有多少个tick device,我们称之local tick device。当然,有些事情(例如整个系统的负荷计算)不适合在local tick驱动下进行,因此,所有的local tick device中会有一个被选择做global tick device,该device负责维护整个系统的jiffies,更新wall clock,计算全局负荷什么的。
高精度的timer需要高精度的clock event,工作在one shot mode的tick device工提供高精度的clock event。因此,基于one shot mode下的tick device,系统实现了高精度timer,系统的各个模块可以使用高精度timer的接口来完成定时服务。虽然有了高精度timer的出现, 内核并没有抛弃老的低精度timer机制(内核开发人员试图整合高精度timer和低精度的timer,不过失败了,所以目前内核中,两种timer是同时存在的)。当系统处于高精度timer的时候(tick device处于one shot mode),系统会setup一个特别的高精度timer(可以称之sched timer),该高精度timer会周期性的触发,从而模拟的传统的periodic tick,从而推动了传统低精度timer的运转。因此,一些传统的内核模块仍然可以调用经典的低精度timer模块的接口。
时间子系统的文件整理
inux kernel 时间子系统的源文件位于linux/kernel/time/目录下,我们整理如下:
这里写图片描述

在Linux内核中有两种不同的clock设备,一种是clock source设备,另一种是clock event设备。Clock source设备一般是一个根据固定频率不停增加的计数器,内核利用该设备可以计算出从系统启动到当前所经过的时间,再加上RTC所提供的初始时间就能得到当前时间(墙上时间)。Clock event设备则用来提供中断,Clock event设备可以配置为按固定周期发生中断(periodic)或者产生一个特定时间间隔后的中断(onshot)。

二.Clock Source设备
数据结构:
struct clocksource 结构对真实的时钟源进行软件抽象。
API:clocksource_register_hz()
注册流程
这里写图片描述
由上图可见,最终大部分工作会转由__clocksource_register_scale完成,该函数首先完成对mult和shift值的计算,然后根据mult和shift值,最终通过clocksource_max_deferment获得该clocksource可接受的最大IDLE时间,并记录在clocksource的max_idle_ns字段中。clocksource_enqueue函数负责按clocksource的rating的大小,把该clocksource按顺序挂在全局链表clocksource_list上,rating值越大,在链表上的位置越靠前。
每次新的clocksource注册进来,都会触发clocksource_select函数被调用,它按照rating值选择最好的clocksource,并记录在全局变量curr_clocksource中,然后通过timekeeping_notify函数通知timekeeping,当前clocksource已经变更。
当有新的clocksource被注册时,除了会挂在全局链表clocksource_list外,还会同时挂在一个watchdog链表上:watchdog_list。定时器周期性地(0.5秒)检查watchdog_list上的clocksource,WATCHDOG_THRESHOLD的值定义为0.0625秒,如果在0.5秒内,clocksource的偏差大于这个值就表示这个clocksource是不稳定的,定时器的回调函数通过clocksource_watchdog_kthread线程标记该clocksource,并把它的rate修改为0,表示精度极差。

三.提供给其他driver计时用的接口函数
为何会有timecounter和cyclecounter?在内核的driver中,我们可能有这样的需求:获取drive中的A事件和B事件之间的时间值或者一个event stream过程中,各个event的时刻值。这里,driver不关心绝对的时间点,关心的是事件之间的时长。为了应对这个需求,clock source模块提供了timecounter和cyclecounter。
内核中使用struct cyclecounter 来抽象一个free running的counter,从0开始,不断累加。由于counter的bit数目有限,因此,某个时间后,counter会wraparound,从0继续开始。timecounter是构架在cycle counter之上,使用纳秒这样的时间单位而不是cycle数目,这样的设计会让用户接口变得更加友好,毕竟大家还是喜欢直观的纳秒值。
这里写图片描述
实际上,最开始的时候,内核的确是只有clock source模块,它位于timekeeping模块和硬件之间。但是,其他内核模块也有访问free running counter的需要,这时候,内核开发人员创建了cycle counter和timer counter这样的概念.

四 .timekeeping

imekeeping模块是一个提供时间服务的基础模块。Linux内核提供各种time line,real time clock,monotonic clock、monotonic raw clock等,timekeeping模块就是负责跟踪、维护这些timeline的,并且向其他模块(timer相关模块、用户空间的时间服务等)提供服务,而timekeeping模块维护timeline的基础是基于clocksource模块和tick模块。通过tick模块的tick事件,可以周期性的更新time line,通过clocksource模块、可以获取tick之间更精准的时间信息。
核心数据结构
struct timekeeper
全局变量
static struct timekeeper timekeeper;
static DEFINE_RAW_SPINLOCK(timekeeper_lock);
static seqcount_t timekeeper_seq;
static struct timekeeper shadow_timekeeper;
timekeeper维护了系统的所有的clock。一个全局变量(共享资源)没有锁保护怎么行,timekeeper_lock和timekeeper_seq都是用来保护timekeeper的,用在不同的场合。
shadow_timekeeper主要用在更新系统时间的过程中。在update_wall_time中,首先将时间调整值设定到shadow_timekeeper中,然后一次性的copy到真正的那个timekeeper中。这样的设计主要是可以减少持有timekeeper_seq锁的时间(在更新系统时间的过程中),不过需要注意的是:在其他的过程中(非update_wall_time),需要sync shadow timekeeper。
获取时间
timekeeper提供了一系列的接口用于获取各种时间信息。
void getboottime(struct timespec *ts); 获取系统启动时刻的实时时间
void get_monotonic_boottime(struct timespec *ts); 获取系统启动以来所经过的时间,包含休眠时间
ktime_t ktime_get_boottime(void); 获取系统启动以来所经过的c时间,包含休眠时间,返回ktime类型
ktime_t ktime_get(void); 获取系统启动以来所经过的c时间,不包含休眠时间,返回ktime类型
void ktime_get_ts(struct timespec *ts) ; 获取系统启动以来所经过的c时间,不包含休眠时间,返回timespec结构
unsigned long get_seconds(void); 返回xtime中的秒计数值
struct timespec current_kernel_time(void); 返回内核最后一次更新的xtime时间,不累计最后一次更新至今clocksource的计数值
void getnstimeofday(struct timespec *ts); 获取当前时间,返回timespec结构
void do_gettimeofday(struct timeval *tv); 获取当前时间,返回timeval结构
五.clockevent
数据结构
struct clock_event_device
enum clock_event_mode
全局变量
static LIST_HEAD(clockevent_devices);
static LIST_HEAD(clockevents_released);
clock event device core模块使用两个链表来管理系统中的clock event device,一个是clockevent_devices链表,该链表中的clock event device都是当前active的device。active的clock device有两种情况,一种是cpu core的current clockevent device,这些device是各个cpu上产生tick的那个clock event device,还有一些active的device由于优先级比较低,当前没有使用,不过可以作为backup的device。另外一个是clockevents_released链表,这个链表中clock event device都是由于种种原因,无法进入active list,从而挂入了该队列。
API 接口:
1.向上层提供的接口:
clockevents_program_event:设定clock event device的触发event的时间参数
clockevents_exchange_device:更换clock event设备
2.向底层clockevent chip driver提供的接口
clockevents_config:配置clock event device
clockevents_register_device:注册clock event device
3.用户空间接口
sysfs接口初始化:clockevents_init_sysfs
显示current tick device:sysfs_show_current_tick_dev
主要作用
这里写图片描述
六.tick device
数据结构
struct tick_device
从上面的定义就可以看出:所谓tick device其实就是工作在某种模式下的clock event设备。工作模式体现在tick device的mode成员,evtdev指向了和该tick device关联的clock event设备。tick device可以工作在两种模式下,一种是周期性tick模式,另外一种是one shot模式。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值