|
|
by echofrompat@linuxsir
转载请勿删除上行。
前几天大家热呼呼地说到如何能知道自己的CPU是32位的还是64位的,小弟专门看了看有关CPUID的东东,在这里给大家参考:
x86的指令集现已成为工业标准,Intel的Pentium系列和AMD的Opteron等的芯片都支持这些指令集。你可能发现,其它的CPU,如VIA的C3也是支持x86指令集的。编译器,如gcc,都可以默认使用x86指令集生成可执行的二元码。这种基于x86最简指令集的二元码都可以在x86兼容的Intel,AMD或是VIA的CPU上运行。
我们知道,Pentium有的MMX和SSE这样的扩展指令器,AMD有3D!NOW,新发布的x86_64 CPU,如AMD64和EM64t,它们都在x86的指令上做了一定的扩充。问题是,如果我们使用的是一个比486更好的CPU,但跑在其上的二元码只使用486的指令器,显然不可能做到运行速度上的最优化。另外,兼容的64位机器上运行32位的二元码,在扩展了指令集的同时还使用到了新的寄存器,问题就更复杂了一些。现在,我们的目的是要让编译器在生成二元码时使用CPU的扩展功能,但不同厂家的CPU有不同的扩展指令集,二元码在使用的特定的扩展指令集后容易在不同的CPU上产生运行期的错误。这就引出了二元码可移植性的问题。
所以我们下载Linux下的rpm包时,有i386,i586,i686之分。i386只使用x86基本指令集,i586对Pentium及与其兼容的CPU做了指令集的优化,i686对Pentium Pro及与其兼容的CPU做了指令集的优化。
那么,编译器是如何知道CPU的特性,并能针对不同的CPU,生成最优的二元码呢?秘密就在于CPUID。
CPUID (central processing unit indentification)其实也是一个CPU的指令。它让OS和程序可以检测是否与CPU兼容,并根据不同的CPU,选择正确的执行路径(execution paths),动态库。
对于EAX寄存器里定义的不同的输入参数,CPUID可以两组信息。如果EAX里存放的是0,CPUID就把生产商的信息放入EBX,ECX,和EDX三个寄存器中。如果EAX里的是1,则这三个寄存器里放入的是处理器特性的标识。
处理器生产商的信息很好说。不外于Intel, AMD, 和VIA。它们都喜欢在这里做广告,如Intel的CPU会返回"GenuineIntel"(真诚到永远!),AMD的CPU会返回"AuthenticAMD"(值得信赖!)。
而对于用户和开发者,更重要的是处理器的功能上的特性。这些特性在EAX寄存器为1时,由CPUID指令将这些处理器特性的标识加载到EBX,ECX和EDX寄存器中。如,EDX中的bit 23指的是64位的MMX指令集,bit 25为SSE,26为SSE2。这里附上AMD64 CPU的程序指南以供大家参考:
http://www.amd.com/us-en/Process ... 82_739_7044,00.html
真相大白! gcc就是使用CPUID来检测CPU类型的, /proc/cpuinfo也是如此。Intel的编译器,icc,甚至可以生成统一的二元码,在执行器检测CPU类型,并选择优化过的程序段给于执行。
下面的是一个具体的检查CPU信息的实现:
- /* small utility to extract CPU information
- Used by configure to set CPU optimization levels on some operating
- systems where /proc/cpuinfo is non-existent or unreliable. */
- #include <stdio.h>
- #include <sys/time.h>
- #ifdef __MINGW32__
- #include <sys/timeb.h>
- void gettimeofday(struct timeval* t,void* timezone)
- { struct timeb timebuffer;
- ftime( &timebuffer );
- t->tv_sec=timebuffer.time;
- t->tv_usec=1000*timebuffer.millitm;
- }
- #define MISSING_USLEEP
- #define sleep(t) _sleep(1000*t);
- #endif
- #ifdef __BEOS__
- #define usleep(t) snooze(t)
- #endif
- #ifdef M_UNIX
- typedef long long int64_t;
- #define MISSING_USLEEP
- #else
- #include <inttypes.h>
- #endif
- typedef struct cpuid_regs {
- unsigned int eax;
- unsigned int ebx;
- unsigned int ecx;
- unsigned int edx;
- } cpuid_regs_t;
- static cpuid_regs_t
- cpuid(int func) {
- cpuid_regs_t regs;
- #define CPUID ".byte 0x0f, 0xa2; "
- asm("push %%ebx; "
- "movl %4,%%eax; " CPUID
- "movl %%eax,%0; movl %%ebx,%1; movl %%ecx,%2; movl %%edx,%3; "
- "pop %%ebx"
- : "=m" (regs.eax), "=m" (regs.ebx), "=m" (regs.ecx), "=m" (regs.edx)
- : "g" (func)
- : "%eax", "%ecx", "%edx");
- return regs;
- }
- static int64_t
- rdtsc(void)
- {
- unsigned int i, j;
- #define RDTSC ".byte 0x0f, 0x31; "
- asm(RDTSC : "=a"(i), "=d"(j) : );
- return ((int64_t)j<<32) + (int64_t)i;
- }
- static void
- store32(char *d, unsigned int v)
- {
- d[0] = v & 0xff;
- d[1] = (v >> 8) & 0xff;
- d[2] = (v >> 16) & 0xff;
- d[3] = (v >> 24) & 0xff;
- }
- int
- main(int argc, char **argv)
- {
- cpuid_regs_t regs, regs_ext;
- char idstr[13];
- unsigned max_cpuid;
- unsigned max_ext_cpuid;
- unsigned int amd_flags;
- char *model_name = "Unknown CPU";
- int i;
- char processor_name[49];
- regs = cpuid(0);
- max_cpuid = regs.eax;
- /* printf("%d CPUID function codes\n", max_cpuid+1); */
- store32(idstr+0, regs.ebx);
- store32(idstr+4, regs.edx);
- store32(idstr+8, regs.ecx);
- idstr[12] = 0;
- printf("vendor_id\t: %s\n", idstr);
- if (strcmp(idstr, "GenuineIntel") == 0)
- model_name = "Unknown Intel CPU";
- else if (strcmp(idstr, "AuthenticAMD") == 0)
- model_name = "Unknown AMD CPU";
- regs_ext = cpuid((1<<31) + 0);
- max_ext_cpuid = regs_ext.eax;
- if (max_ext_cpuid >= (1<<31) + 1) {
- regs_ext = cpuid((1<<31) + 1);
- amd_flags = regs_ext.edx;
- if (max_ext_cpuid >= (1<<31) + 4) {
- for (i = 2; i <= 4; i++) {
- regs_ext = cpuid((1<<31) + i);
- store32(processor_name + (i-2)*16, regs_ext.eax);
- store32(processor_name + (i-2)*16 + 4, regs_ext.ebx);
- store32(processor_name + (i-2)*16 + 8, regs_ext.ecx);
- store32(processor_name + (i-2)*16 + 12, regs_ext.edx);
- }
- processor_name[48] = 0;
- model_name = processor_name;
- }
- } else {
- amd_flags = 0;
- }
- if (max_cpuid >= 1) {
- static struct {
- int bit;
- char *desc;;
- char *description;
- } cap[] = {
- { 0, "fpu", "Floating-point unit on-chip" },
- { 1, "vme", "Virtual Mode Enhancements" },
- { 2, "de", "Debugging Extension" },
- { 3, "pse", "Page Size Extension" },
- { 4, "tsc", "Time Stamp Counter" },
- { 5, "msr", "Pentium Processor MSR" },
- { 6, "pae", "Physical Address Extension" },
- { 7, "mce", "Machine Check Exception" },
- { 8, "cx8", "CMPXCHG8B Instruction Supported" },
- { 9, "apic", "On-chip CPIC Hardware Enabled" },
- { 11, "sep", "SYSENTER and SYSEXIT" },
- { 12, "mtrr", "Memory Type Range Registers" },
- { 13, "pge", "PTE Global Bit" },
- { 14, "mca", "Machine Check Architecture" },
- { 15, "cmov", "Conditional Move/Compare Instruction" },
- { 16, "pat", "Page Attribute Table" },
- { 17, "pse36", "Page Size Extension 36-bit" },
- { 18, "psn", "Processor Serial Number" },
- { 19, "cflsh", "CFLUSH instruction" },
- { 21, "ds", "Debug Store" },
- { 22, "acpi", "Thermal Monitor and Clock Ctrl" },
- { 23, "mmx", "MMX Technology" },
- { 24, "fxsr", "FXSAVE/FXRSTOR" },
- { 25, "sse", "SSE Extensions" },
- { 26, "sse2", "SSE2 Extensions" },
- { 27, "ss", "Self Snoop" },
- { 29, "tm", "Therm. Monitor" },
- { -1 }
- };
- static struct {
- int bit;
- char *desc;;
- char *description;
- } cap_amd[] = {
- { 22, "mmxext","MMX Technology (AMD Extensions)" },
- { 30, "3dnowext","3Dnow! Extensions" },
- { 31, "3dnow", "3Dnow!" },
- { 32, "k6_mtrr", "Memory Type Range Registers" },
- { -1 }
- };
- int i;
- regs = cpuid(1);
- printf("cpu family\t: %d\n"
- "model\t\t: %d\n"
- "stepping\t: %d\n" ,
- (regs.eax >> 8) & 0xf,
- (regs.eax >> 4) & 0xf,
- regs.eax & 0xf);
-
- printf("flags\t\t:");
- for (i = 0; cap[i].bit >= 0; i++) {
- if (regs.edx & (1 << cap[i].bit)) {
- printf(" %s", cap[i].desc);
- }
- }
- for (i = 0; cap_amd[i].bit >= 0; i++) {
- if (amd_flags & (1 << cap_amd[i].bit)) {
- printf(" %s", cap_amd[i].desc);
- }
- }
- printf("\n");
- if (regs.edx & (1 << 4)) {
- int64_t tsc_start, tsc_end;
- struct timeval tv_start, tv_end;
- int usec_delay;
- tsc_start = rdtsc();
- gettimeofday(&tv_start, NULL);
- #ifdef MISSING_USLEEP
- sleep(1);
- #else
- usleep(100000);
- #endif
- tsc_end = rdtsc();
- gettimeofday(&tv_end, NULL);
- usec_delay = 1000000 * (tv_end.tv_sec - tv_start.tv_sec)
- + (tv_end.tv_usec - tv_start.tv_usec);
- printf("cpu MHz\t\t: %.3f\n",
- (double)(tsc_end-tsc_start) / usec_delay);
- }
- }
- printf("model name\t: %s\n", model_name);
- exit(0);
- }
复制代码 |
|