如何获取CPU的主频和使用率?

发布时间:2014-10-25 2:20:59
来源:分享查询网

最近看到论坛中有人需要在Java端读取系统的CPU主频和使用率,在网上搜索了一下,发现了一些有用的东西,就在这总结一下。 一、获得CPU的主频 1、从注册表中读取 在注册表中键HKLM/HARDWARE/DESCRIPTION/System/CentralProcessor/0/~MHz对应的值为CPU的主频。 但是这种方法有一定的局限性,就是有的系统上没有这个键值。 2、通过写JNI调用汇编代码获得 //Cpuid.h 类声明 #ifndef _CPUID_H #define _CPUID_H #include <string> #include <windows.h> using namespace std; typedef unsigned long DWORD; typedef __int64 LONGLONG; class CPUID { public: CPUID(); virtual ~CPUID(); string GetVID(); //获取cpu制造商 string GetBrand(); //获取cpu型号 LONGLONG GetFrequency(DWORD sleepTime = 1000);//获取cpu主频 private: void Executecpuid(DWORD veax); //用以执行cpuid指令 DWORD m_eax; // 存储返回的eax DWORD m_ebx; // 存储返回的ebx DWORD m_ecx; // 存储返回的ecx DWORD m_edx; // 存储返回的edx }; #endif //Cpuid.cpp 类实现 #include "Cpuid.h" static const int S = sizeof(DWORD); CPUID::CPUID() :m_eax(0),m_ebx(0),m_ecx(0),m_edx(0) { } CPUID::~CPUID() { } void CPUID::Executecpuid(DWORD veax) { // 因为嵌入式的汇编代码不能识别 类成员变量 // 所以定义四个临时变量作为过渡 DWORD deax; DWORD debx; DWORD decx; DWORD dedx; __asm { mov eax, veax cpuid mov deax, eax mov debx, ebx mov decx, ecx mov dedx, edx } m_eax = deax; // 把临时变量中的内容放入类成员变量 m_ebx = debx; m_ecx = decx; m_edx = dedx; } string CPUID::GetVID() { char cVID[S*3+1]; // 字符串,用来存储制造商信息 memset(cVID, 0, sizeof(cVID)); // 把数组清0 Executecpuid(0); // 执行cpuid指令,使用输入参数 eax = 0 memcpy(cVID+S*0, &m_ebx, S); // 复制前四个字符到数组 memcpy(cVID+S*1, &m_edx, S); // 复制中间四个字符到数组 memcpy(cVID+S*2, &m_ecx, S); // 复制最后四个字符到数组 return string(cVID); // 以string的形式返回 } string CPUID::GetBrand() { const DWORD BRANDID = 0x80000002; // 从0x80000002开始,到0x80000004结束 char cBrand[S*4*3+1]; // 用来存储商标字符串,48个字符 memset(cBrand, 0, sizeof(cBrand)); // 初始化为0 for (DWORD i = 0; i < 3; i++) // 依次执行3个指令 { Executecpuid(BRANDID + i); // 每次执行结束后,保存四个寄存器里的asc码到数组 memcpy(cBrand + i*S*4 + S*0, &m_eax, S); memcpy(cBrand + i*S*4 + S*1, &m_ebx, S); memcpy(cBrand + i*S*4 + S*2, &m_ecx, S); memcpy(cBrand + i*S*4 + S*3, &m_edx, S); } return string(cBrand); // 以string的形式返回 } LONGLONG CPUID:: GetFrequency(DWORD sleepTime) { HANDLE hp = ::GetCurrentProcess(); //获取当前进程 HANDLE ht = ::GetCurrentThread(); //获取当前线程 DWORD pc = ::GetPriorityClass( hp ); //获取当前进程优先度 DWORD tp = ::GetThreadPriority( ht ); //获取当前线程优先度 BOOL flag1 = FALSE , flag2 = FALSE; DWORD low1 = 0 , high1 = 0 , low2 = 0 , high2 = 0; flag1 = ::SetPriorityClass( hp , REALTIME_PRIORITY_CLASS ); //将当前进程优先度设置最高 flag2 = ::SetThreadPriority( ht , THREAD_PRIORITY_HIGHEST ); //将当前线程优先度设置最高 ::Sleep( 10 ); LARGE_INTEGER fq,st,ed; ::QueryPerformanceFrequency(&fq); //精确计时 ::QueryPerformanceCounter(&st); //获得起始时间 __asm //获得当前cpu的时间周期数 { rdtsc mov low1 , eax mov high1 , edx } ::Sleep( sleepTime ); //将线程挂起片刻 ::QueryPerformanceCounter(&ed); //获得结束时间 __asm //获得当前cpu的时间周期数 { rdtsc mov low2 ,eax mov high2 ,edx } if( flag1 ) ::SetPriorityClass( hp , pc ); //将当前进程优先度恢复原样 if( flag2 ) ::SetThreadPriority( ht , tp ); //将当前线程优先度恢复原样 ::CloseHandle( hp ); ::CloseHandle( ht ); //将cpu的时间周期数转化成64位整数 LONGLONG begin = (LONGLONG)high1<<32 | low1; LONGLONG end = (LONGLONG)high2<<32 | low2; //将两次获得的cpu时间周期数除以间隔时间,即得到cpu的频率。 //由于windows的Sleep函数有大约15毫秒的误差,故以windows的精确计时为准 return (end - begin)*fq.QuadPart/(ed.QuadPart-st.QuadPart); } //Cpu.cpp 主cpp #include <iostream> #include "Cpuid.h" #define GHZ_UNIT (1000*1000*1000) #define MHZ_UNIT (1000*1000) int main() { CPUID cpu; string vid = cpu.GetVID(); cout<<vid<<endl; string brand = cpu.GetBrand(); cout<<brand<<endl; LONGLONG freq = cpu.GetFrequency(); if (freq > GHZ_UNIT) { double freqGHz = (double)freq/GHZ_UNIT; printf("%.2fGHz/n",freqGHz); //保留小数点后两位输出 } else { cout<<freq/MHZ_UNIT<<"MHz"<<endl; } cin.get(); return 0; }   3、通过CPUID来获取 #include <stdio.h> int main() { unsigned int eflags1, eflags2 = 0; unsigned int eax = 0; unsigned int ebx,ecx,edx; /** * 测试CPU是否支持CPUID指令。 * eflags寄存器的第21位,如果程序可以清楚/设置它,则说明CPU支持CPUID指令。否则不支持 */ /* 先取eflags */ asm volatile ("pushf/n/t" "popl %%eax" : "=a"(eflags1) : : "memory" ); printf("original eflags is %p/n", eflags1); /* 把eflags的第21位取反,写回寄存器中 */ asm volatile ("pushl %0/n/t" "popf" : : "g"(eflags1 & ~( eflags1 & (1<<21) ) ) ); /* 检查一下现在的eflags,确认第21位和最初的值相反 */ asm volatile ("pushf/n/t" "popl %%eax" : "=a"(eflags2) : : "memory" ); printf("modified eflags is %p/n", eflags2); /* 把原来的eflags值设置回去 */ asm volatile ("pushl %0/n/t" "popf" : : "g"(eflags1) ); /** * FIXME: Intel文档并没有说,如果不支持CPUID的话,clear/set eflags的第21位会有什么错误。 * 它只说,在不支持CPUID指令的CPU上,如80386,执行CPUID会产生invalid opcode错误 * * 所以,在这里我们不处理 读/写 eflags 第21比特失败的情形 */ /** * eax == 1,则在eax中返回Family/Model/Stepping等信息 * [0:3] stepping * [4:7] model * [8:11] family * [12:13] processor type * [16:19] extended model ID * [20:27] extended family ID */ asm volatile ("cpuid" : "=a"(eax) : "0"(1) ); // printf("eax is %p/n", eax); printf("Extended Family/t: %d/n", (0xff00000 & eax) >> 20); printf("Extended Model/t: %d/n", (0xf0000 & eax) >> 16); printf("Processor type/t: %d/n", (0x3000 & eax) >> 12); printf("Family/t/t: %d/n", (0xf00 & eax) >> 8); printf("Model/t/t: %d/n", (0xf0 & eax) >> 4); printf("Stepping:/t: %d/n", (0xf & eax)); printf("/n"); /** * eax == 0x800000000 * 如果CPU支持Brand String,则在EAX中返 >= 0x80000004的值。 */ asm volatile ("cpuid" : "=a"(eax) : "0"(0x80000000) ); printf("Is CPU support Brand String? %s/n", eax >= 0x80000004? "yes":"no"); printf("/n"); /** * 如果支持Brand String,则EAX从0x80000002到0x80000004,每次增1,CPUID指令返回: * EAX : Processor Brand String * EBX : Processor Brand String Continued * ECX : Processor Brand String Continued * EDX : Processor Brand String Continued */ if(eax >= 0x80000004) { unsigned int brands[4]; //每次的eax、ebx、ecx、edx unsigned int i; printf("Brand String/t: "); for (i = 0x80000002; i <= 0x80000004; i++) { asm volatile ("cpuid" : "=a"(brands[0]), "=b"(brands[1]), "=c"(brands[2]), "=d"(brands[3]) : "0" (i) ); printf("%s", (char *)brands); } //FIXME: 打出来的字符串是:In^Htel(R) Pentium(R^H) D CPU 2.80GHz //其中^H是个不可见字符,会把它前一个吃掉 printf("/n"); } /** * eax == 0 * eax : cpuid指令允许的最大eax输入值 * ebx : "Genu" * ecx : "ntel" * edx : "inel" */ asm volatile ("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "0"(0) ); printf("Maximum CPUID Input EAX : %p/n", eax); char string[128]; snprintf(string, 5, "%s", (char *)&ebx); snprintf(string + 4, 5, "%s", (char *)&edx); snprintf(string + 8, 5, "%s", (char *)&ecx); printf("Vendor/t/t: %s/n", string); printf("/n"); /** * eax == 1, * edx的第18比特为1,则CPU支持serial number * 为0,则不支持,或者被disabled * 序列号有96位,其中最高32位即是eax的输出值。应当把它保存下来,然后 * 再设置eax==3, 取剩下的64位 */ asm volatile ("cpuid" : "=a"(eax), "=d"(edx) : "a"(1) ); if ( edx & (1 << 18) ) { /* serial number supported */ /* edx输出中间32位的序列号,ecx输出最低32位的序列号 */ asm volatile ("cpuid" : "=c"(ecx), "=d"(edx) : "a"(3) ); printf("Serial Number/t : %x-%x-%x-%x-%x-%x/n", eax >> 16, eax << 16, edx >> 16, edx << 16, ecx >> 16, ecx << 16); } else printf("Serial Number not supported./n"); printf("/n"); /** * eax == 80000006h,返回L2 Cache的信息 * * ecx[31:16] : L2 Cache size, in Kbytes * ecx[15:12] : L2 Cache Associativity * 00h disabled * 01h direct mapped * 02h 2-Way * 04h 4-Way * 06h 8-Way * 08h 16-Way * 0Fh Fully associative * ecx[7:0] : L2 Cache Line size in bytes */ asm volatile ("cpuid" : "=c"(ecx) : "a"(0x80000006) ); printf("L2 Cache Size/t : %dKbytes/n", ( ecx >> 16 ) ); printf("L2 Cache Line Size/t : %dbytes/n", (ecx & 0xff)); printf("L2 Cache Associativity/t : "); switch ( (ecx & 0xf000) >> 12 ) { case 0x00: printf("%s/n", "disabled"); break; case 0x01: printf("%s/n", "direct mapped"); break; case 0x02: printf("%s/n", "2-Way"); break; case 0x04: printf("%s/n", "4-Way"); break; case 0x06: printf("%s/n", "8-Way"); break; case 0x08: printf("%s/n", "16-Way"); break; case 0x0f: printf("Fully associative"); break; default: printf("No such entry.../n"); } printf("/n"); /** * Input : eax == 4 && ecx == 0 * * (eax[31:26] + 1) 是该物理处理器package上实现的core CPUs数目 */ asm volatile ("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "0"(4), "2"(0) ); printf("Number of Cores on this physical package/t : %d/n", (eax >> 27) + 1 ); printf("/n"); /** * Input : eax == 1,则edx返回feature flag * */ return 0; }   二、获得CPU使用率 参考http://www.java2000.net/p11161

返回顶部
查看电脑版