您的位置:首页 > 其它

内核检测CPU的时钟频率

2011-05-10 21:10 253 查看
要想测出CPU的时钟频率,首先得了解TSC的意义,在CPU内部有一个64位的寄存器,每来一个CPU的时钟信号,tsc计数器的值加一,因此可以用以下方法来确认cpu的始终频率:通过计算一定时间内的tsc计数器的增值,可以得出1S钟tsc增值,这个增值就是CPU的时钟频率。下面就

以一个看过的实时hypervisor的获取频率例子来解释:

/*假设时钟频率是100HZ,那么一个滴答数就是10ms。*/
#define CALIBRATE_LATCH (5 * LATCH)
/*下面会把这个值写入8254的第二个计数器,这时需要50ms计数器的值归零*/
#define CALIBRATE_TIME (5 * 1000020/HZ)
/*约等于5000000/1000=5000,也就是5ms*/
static unsigned long calibrate_tsc(void) {
/* Set the Gate high, disable speaker */
outb((inb(0x61) & ~0x02) | 0x01, 0x61);

/*
* Now let's take care of CTC channel 2
*
* Set the Gate high, program CTC channel 2 for mode 0,
* (interrupt on terminal count mode), binary count,
* load 5 * LATCH count, (LSB and MSB) to begin countdown.
*
* Some devices need a delay here.
*/
outb(0xb0, 0x43);                       /* binary, mode 0, LSB/MSB, Ch 2 */
outb_p(CALIBRATE_LATCH & 0xff, 0x42);   /* LSB of count */
outb_p(CALIBRATE_LATCH >> 8, 0x42);     /* MSB of count */

{
unsigned long startlow, starthigh;
unsigned long endlow, endhigh;
unsigned long count = 0;

rdtsc(startlow,starthigh);
do {
count++;
} while ((inb_p(0x61) & 0x20) == 0);
rdtsc(endlow,endhigh);
/*上面这段代码是5ms里面tsc增值,因为5ms到时,8254的第二个计数器值归0*/
/* Error: ECTCNEVERSET */
if (count <= 1)
goto bad_ctc;

/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0/n/t"
"sbbl %3,%1"
:"=a" (endlow), "=d" (endhigh)
:"g" (startlow), "g" (starthigh),
"0" (endlow), "1" (endhigh));

/* Error: ECPUTOOFAST */
if (endhigh)
goto bad_ctc;

/* Error: ECPUTOOSLOW */
if (endlow <= CALIBRATE_TIME)
goto bad_ctc;

__asm__("divl %2"
:"=a" (endlow), "=d" (endhigh)
:"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
/*上面这段汇编是用5ms=5000微秒/tsc增值,得到的endlow代表一个增值耗费微秒数,假设5000/tsc增值=X,则一微秒发生的tsc个数为1/X,那么1秒则为1000000/x,这个就是cpu始终频率*/
return endlow;
}

/*
* The CTC wasn't reliable: we got a hit on the very first read,
* or the CPU was so fast/slow that the quotient wouldn't fit in
* 32 bits..
*/
bad_ctc:
return 0;
}


上述代码获得一个1/x,在下面的代码里面就正式求出cpu始终频率了。

static int init_tsc (void) {
unsigned long tsc_quotient = calibrate_tsc ();
unsigned int cpu_khz = get_cpu_khz (tsc_quotient);
printk("<XtratuM> Detected %u.%03u MHz processor./n",
cpu_khz / 1000, cpu_khz % 1000);

hw_hz = cpu_hz = (unsigned long long)cpu_khz * 1000;
return 0;
}
'''''
'''
static unsigned int get_cpu_khz (unsigned long tsc_quotient) {
unsigned int cpu_khz;
unsigned long eax=0, edx=1000;
__asm__("divl %2"
:"=a" (cpu_khz), "=d" (edx)
:"r" (tsc_quotient),
"0" (eax), "1" (edx));
/*上面汇编是求千赫兹,在之前的calibrate_tsc里面得到一微秒发生1/x个tsc增量,calibrate_tsc返回的是x,则知道CPU时钟频率是1000000/x,则1000/x肯定是KHZ了,同理1/x为MHZ了*/
return cpu_khz;
}


具体的过程已经在上面的代码做了注释,具体的关于LATCH等概念,可以参考我之前写的linux的几种时钟比较,或者可以google或者百度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: