Linux内核定时器--原版

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
• 由于定时器异步执行,因而定时器处理函 数必须进行互斥保护。
2.在了解定时器之前先了解三个概念
• 节拍率:HZ • Jiffies • 时钟中断处理程序
2.1 节拍率:HZ
• 节拍率(HZ)是时钟中断的频率,表示一 秒钟中断的次数。 例如:HZ=100,表示一秒内触发100次时钟 中断程序。 节拍率是通过静态预处理定义的,在系统 启动时按照HZ值对硬件进行设置。 不同的体系结构,HZ值也不同。
Jiffies定义于文件<linux/jiffies.h>中,格式如下: extern u64 __jiffy_data jiffies_64; extern unsigned long volatile __jiffy_data jiffies; 使用例子如下: unsigned long time_tick = jiffies + 1; //从现在开始1个节拍 unsigned long later = jiffies + 5 * HZ; //从现在开始5秒
2.3 用户空间的HZ
内核中使用的HZ和用户空间定义的HZ 值可能不一致,为了避免这种错误,内核 中也定义了USER_HZ,即用户空间的HZ值。 • 一般来说,USER_HZ和HZ都是相差整数倍, 内核中通过函数jiffies_to_clock_t来将内核中 的jiffies转为用户空间的jiffies。
Linux 内核机制-定时器
参考资料: 《Linux内核设计与实现》(美)Robert Love著, 陈莉君 康华 译 原文第三版 , 机械工业出版社。
《深入Linux内核架构》 (德)Wolfgang Mauerer 著 , 郭旭 译 ,人民邮电出版社。
《深入理解Linux内核》 DANIEL P.BOVET & MARCO CESATI 著,陈莉君 张琼声 张宏伟 译 ,中国电力出版社。
• unsigned long timeout = jiffies + HZ/2; • while (time_after(jiffies, timeout)) • {
/**还没有超时,继续执行任务/
• }
/*超时了,发生错误*/
关于时间的换算
• <jiffies.h> • unsigned int jiffies_to_msecs(const unsigned long j); • unsigned int jiffies_to_usecs(const unsigned long j); • unsigned long msecs_to_jiffies(const unsigned int m) • unsigned long usecs_to_jiffies(const unsigned int u);
• 时钟中断频率越高,处理器必须花时间来 执行中断处理程序,占用CPU时间过多。
• 减少了处理器对其他工作的时间,更频繁 的打乱处理器高速缓存并增加耗电。
2.2 jiffies
• 全局变量jiffies用来记录自系统启动以来产 生得节拍的总数。例如:系统启动了N秒, 那么jiffies就为N x HZ。 • Jiffies的类型为无符号长整型(unsigned long),用其它任何类型存放都不正确。
• void update_process_times(int user_tick) • { • struct task_struct *p = current; • int cpu = smp_processor_id();
• • • • • • • • • • } /* 更新当前进程占用CPU的时间 */ account_process_tick(p, user_tick); /* 同时触发软中断,处理所有到期的定时器 */ run_local_timers(); rcu_check_callbacks(cpu, user_tick); printk_tick(); /* 减少当前进程的时间片数 */ scheduler_tick(); run_posix_cpu_timers(p);
高HZ的优点
• 内核定时器能够以更高的频度和更高的准 确度运行。 • 依赖定时值执行的系统调用,比如poll() 和select(),能够以更高的精度运行。
• 对诸如资源消耗和系统运行时间等的测量 会有更精细的解析度。 • 提高进程抢占的准确度。
高HZ的缺点
• 时钟中断执行的越频繁,系统的负担越高
2.4 时钟中断处理程序
时钟中断处理程序可以划分为两个部 分:体系结构部分和体系结构无关部分。 作为系统定时器的中断处理程序而注 册到内核中,以便产生中断时,它能够相 应的运行。虽然处理程序依赖特定的体系 结构,但是大多数处理程序最低限度也要 执行如下工作:
• 获得xtime_lock锁,以便访问jiffies_64和墙 上时间xtime进行保护 • 需要时应答或重新设置系统时钟 • 周期性地使用墙上时间更新实时时钟
2.2.1 jiffies的回绕
• Jiffies变量总是无符号长整数,因此,在32 位体系结构上时32位,在64位体系结构上 时64位。32位的jiffies变量,如果HZ = 100, 497天后会溢出,如果HZ = 1000,49.7天后 会溢出。而64位则别指望能看到溢出。 • 访问jiffies的代码仅会读取jiffies_64的低32位, 通过get_jiffies_64()函数,可以读取整个64 位,多数代码只需要访问低32位就够了。
• update_process_times(user_mode(get_irq_regs())); • profile_tick(CPU_PROFILING); • }
其中重要的函数进行了注释
• void do_timer(unsigned long ticks) • { • /* jiffies_64 增加指定ticks */ • jiffies_64 += ticks; • /* 更新实际时间 */ • update_wall_time(); • /* 更新系统的平均负载值 */ • calc_global_load(); • }
对于32位无符号长整型,最大取值为 2^32-1。所以在溢出之前,定时器节拍计数最 大为4294967295。如果节拍计数达到了最大值 后还要继续增加的话,它的值就会回绕到0。 比如下面的代码: unsigned long timeout = jiffies + HZ/2; //设置超时时间为0.5秒 while (timeout < jiffies) { /*还没有超时,继续执行任务*/ } /*超时了,发生错误*/
内核提供四个宏来帮助比较节拍计数,能正确 处理节拍计数的回绕问题
• 位于<linux/jiffies.h>文件中提供的time_after, time_before等宏 • #define time_after(a,b) \ (typecheck(unsigned long, a) && \ typecheck(unsigned long, b) && \ ((long)(b) - (long)(a) < 0)) • #define time_before(a,b) time_after(b,a) • #define time_after_eq(a,b) \ (typecheck(unsigned long, a) && \ typecheck(unsigned long, b) && \ ((long)(a) - (long)(b) >= 0)) • #define time_before_eq(a,b) time_after_eq(b,a)
提高节拍率等同于提供啊中断解析度。 例如:HZ=100的时钟执行粒度为10ms,, 即系统中周期事件最快为每10ms运行一次, 而不可能有更高的精度,但是当HZ = 1000 时,解析度就为1ms—精细了10倍。虽然内 核可以提供频度为1ms的时钟,但是并没有 证据显示对系统中所有的程序而言,频率 为1000HZ比100HZ的时钟更合适。
注意点:
• 在X86体系结构中,系统HZ默认值为100 • 内核在<asm/param.h>文件中定义了这个值 • 在编写内核代码时,HZ值不是一个固定不 变的值,大多数体系结构的HZ值是可调的
2.1.1 理想的HZ值
自Linux问世以来,i386体系结构中时 钟中断频率就设定为100HZ,但是在2.5开发 版本内核中,中断频率被提高到1000HZ。 提高节拍率意味着时钟中断产生得更 加频繁,所以中断处理程序也会更频繁的 执行,带来以下几点好处: • 更高的时钟中断解析度可提高时间驱动事 件的解析度。 • 提高了时间驱动事件的准确度。
1. 基本概念
• 在某些场景下,我们需要在特定的时间后 做某些动作,但是又不想一直等待而浪费 CPU,这时定时器是非常重要的。 • 定时器用于在将来某个时间点执行某个函 数以完成特定的任务 。 • Linux中内核定时器是基于软中断实现的, 也就是它处于中断上下文,而非进程上下 文。
在非进程上下文需要遵循的原则
• 调用体系结构无关的时钟例程: tick_periodic()
• /*tick_periodic函数位于:kernel/time/tick_common.c*/ • static void tick_periodic(int cpu) • { • if (tick_do_timer_cpu == cpu) { • write_seqlock(&xtime_lock); • • • • • /* Keep track of the next tick event */ tick_next_period = ktime_add(tick_next_period, tick_period); do_timer(1); write_sequnlock(&xtime_lock); }
• 不允许访问用户空间 • Current无意义,因而也不可用 • 不能进行睡眠护着调度,不能调用schedule 或者某种wait_event,也不能调用任何可能 引起睡眠的函数,信号量也不可用,因为 信号量可能引起休眠。
Hale Waihona Puke Baidu
如何判断中断上下文?
• 内核代码通过调用函数in_interrupt()可以判 断当前是否处于中断上下文,只要返回非0 就表示处于中断上下文。 • 内核可以通过调用in_atomic()判断当前是否 允许调度,不允许调度的情况包括:处于 中断上下文以及拥有自旋锁的上下文。
为什么四个宏能解决回绕问题呢?
• 这跟计算机数据存储形式有关,数据在计 算机中以二进制的补码存储,具体可以编 码测试一下。 • 但是timeout不能大于2^32-1,超过这个值, 这四个宏就出现问题,但是几乎没有那个 程序会一次性delay25天。
把上面得代码改成下面得代码就可以解决回绕问题:
• 正常的情况下,上面的代码没有问题。但是当 jiffies接近最大值的时候,就会出现回绕问题, 如下图所示:
• 1. 循环中第一次比较时,jiffies = J1,没有超时 • 2. 循环中第二次比较时,jiffies = J2,实际已经 超时了,但是jiffies超过最大值后又从0开始, 所以J2远远小于timeout • 3. while循环不会结束,相当于死循环。
• • • • • • • • • • •
/*参见文件: kernel/time.c*/ clock_t jiffies_to_clock_t(long x) { #if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0 # if HZ < USER_HZ return x * (USER_HZ / HZ); # else return x / (HZ / USER_HZ); # endif #else return div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ); #endif } EXPORT_SYMBOL(jiffies_to_clock_t);
相关文档
最新文档