Windows的系统时钟间隔

引用注明>> 【作者张佩】【原文:www.yiiyee.cn/blog


工具下载:http://www.yiiyee.cn/Blog/clock-interval/


系统时钟间隔是个一般不被关心的系统标量,它反映了系统产生时钟中断的频率,间隔越小频率越高,反之亦然。每当时钟中断产生,系统相关的中断函数将会处理这个中断。时钟中断处理函数会更新系统时间,检查内核调试信息等。

1. 线程调度和时钟间隔

系统时钟间隔和另一个极重要的系统标量关联在一起,即系统的线程调度时间。按照Windows系统的设计,线程调度时间被分为普通程序和后台服务两种类型。前者时间长度是2个时钟间隔,后者时间长度是6个时钟间隔。一旦时钟间隔被确定,线程调度时间也就基本确定了。

OS根据平台的不同,定义固定的最小和最大时钟间隔。对于X86平台而言,最小时钟间隔是0.5ms,最大值大概是15.6001ms。在内部,时钟间隔以100ns为单位进行表述,所以0.5ms被表示为5000个100ns单位,15.6ms被表示为156001个100ns单位。

使用ClockInterval工具可以查看/设置系统时钟频率(类似的工具是Sysinternal的ClockRes工具,可以查看当前值)。下图是运行ClockInterval后的界面截图:

图1 获取时钟频率

线程调度时间是一个重要的系统标量,对不用功能的应用而言,它的取值具有矛盾性。如果调度时间太小,系统就会频繁切换线程(时间片用完)而导致性能降低。而如果调度时间太长,某些对于实时性要求强的任务又无法接受。

线程调度时间和时钟间隔的另一个关系是,系统把时钟间隔的1/3作为线程调度的基本时间片段,也就是说,如果一个线程在运行过程中放弃剩余的时间片,则它用掉的和放弃的,都是1/3时钟间隔的倍数。系统默认总是使用最大时钟间隔为当前使用的时钟频率,时钟间隔越大,线程就越可能在一个时间片内完成全部工作,剩下的时间片还可以还给系统重新调度。应放弃剩余时间片而产生的损失,不会操作1/3时钟间隔。

哪些任务是属于实时性能强的呢?音视频软件、实时监控软件等。MediaPlayer是一个强实时要求的音频软件,笔者在本机(Win7 64)做实验发现,每当运行MediaPlayer程序,它都会把系统时钟频率调低到10ms;而当退出MediaPlayer,时钟频率又会恢复至原值。类似的软件还有WinAMP和鲁大师,读者有空可以实测一下,这两款软件会把系统时间间隔设置成1ms。

如果读者使用这些软件在自己的机器上未测试出类似情况,可能是由于读者所使用的软件版本与笔者当前所使用的不一致,笔者对此不做特殊保证。

2. 用户程序

用户程序控制系统时钟间隔,所能使用的最简单的办法是调用Windows MMLib库的接口函数timeBeginPeriod。与timeBeginPeriod相匹配,存在另一个接口函数timeEndPeriod。后来用来将修改后的时钟间隔恢复到原始值。这两个函数原型定义如下:

MMRESULT timeBeginPeriod(UINT uPeriod );
MMRESULT timeEndPeriod(UINT uPeriod );
按文档说明,程序执行期间这两个函数应当成对调用。但据笔者观察,如果退出程序时忘记调用timeEndPeriod函数,系统时钟间隔也会自动恢复的。这两个函数较简单,下面是一段演示代码:

UINT newInverval = 1;
MMRESULT r = timeBeginPeriod( newInverval ); // 设置当前系统时钟间隔为1ms

//……

if(r == TIMER_NOERROR)
{
  timeEndPeriod(newInterval);// 最后,再恢复到原频率
}
MMLib仅提供了修改时钟间隔的接口,而未提供获取当前时钟间隔的方法,这需要用到另外的Win32API即:GetSystemTimeAdjustment。其函数声明如下:

BOOL WINAPI GetSystemTimeAdjustment(
  __out         PDWORD lpTimeAdjustment,
  __out         PDWORD lpTimeIncrement,
  __out         PBOOL lpTimeAdjustmentDisabled
);

这个函数没有一个参数是用来输入的,全部是输出参数。其中第二个参数lpTimeIncrement返回的是当前的时钟频率(100ns单位)。下面是一段实例代码,它其实也是ClockResV1.0程序的源码:

void main( int argc, char *argv[] ) 
{  
    DWORD adjustment,clockInterval; 
    BOOL  adjustmentDisabled;    
   
    GetSystemTimeAdjustment(&adjustment, 
                            &clockInterval, 
                             &adjustmentDisabled ); 
    printf( "The systemclock interval is %d ms\n", clockInterval / 10000 ); 
}   

除了上述公开的API接口外,另外有两个未文档的NT接口函数NtQueryTimerResolution和NtSetTimerResolution用来获取和设置时钟频率。在此不做详细分析。

3. 恢复时钟间隔

前文说过应成对地使用timeBeginPeriod和timeEndPeriod函数(除非直接退出程序)。忘记调用timeEndPeriod会产生这样的后果:以后任何程序如果要重新调试系统时钟间隔,只能调低,不能调高。

虽然笔者编写的软件ClockInterval其内部并未使用timeBeginPeriod和timeEndPeriod函数,但上述规则却同样适用。可以用ClockInterval做下面的实验:

  1. 开启ClockInterval,假设你得到的结果和图1相同,此时将当前时钟间隔设置成10ms,这一定是可以成功的。
  2. 开启另一个ClockInterval实例,此时将看到当前的时钟间隔是10ms。尝试将当前时钟间隔设置成最大值15.6ms,会发现设置失败。
  3. 关闭第一个ClockInterval实例后,再次尝试,使用第二步中的ClockInterval实例设置最大的时钟间隔,成功!

一般来说,在打开ClockInterval程序后,界面上显示的当前时钟间隔值是你所能重设的时钟间隔的上限。点击ClockInterval界面的test按钮,会显示关于此的更多信息:

 

图2 Test测试

图中信息所示:在系统中的其它进程/线程结束其所设置的1ms时钟间隔前,当前进程无法设置更大时钟间隔(>1ms)。

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值