最近在编写注册模块时,其中机器码的生成使用到了CPUID,而程序运行后,却发现,注册模块的校验并不是每次都有效。多次独立运行,机器码竟然是随机变化的!而当我使用IDE调试环境时,却一切正常。
经过多次观察,发现机器码在两个值之间随机取得(即非此即彼)。而在机器码生成算法中,我并没有使用到随机函数,那么问题的出现只能在使用到的两个硬件数据上。
到这里,问题应该是比较明了了,因为我想起了所用的工作站是双核的(提起双核就让我想到真假双核,呵呵),我试着在程序启动之后在“任务管理器”中手动指定单CPU运行,之后发现,随机产生机器码的问题没有了。
因此可以断定:双核CPU不同核的ID是不一样的(这也从另一个方面证明了我这块P-D 2.8是真双核咯?呵呵),而程序运行后被分配到哪个核执行是随机的,从而造成了机器码(确切的说是CPUID)不相同的问题。同时,在IDE环境调试时一切正常的原因也清楚了:IDE调试环境启动后占用一个CPU核心,而另一个核心去启动要调试的程序,只要IDE调试环境所在CPU核心不变,那该程序所在的核心也不变(此断言没有经过验证,只是我根据现象推断的,也许这个分配是由CPU运行优先级机制控制的)
那么怎么消除这种不确定性呢?一般有两种方法:
1、 指定你的程序占用第一次启动时选中的那个核心。此方法涉及到更多的CPU信息操作,相对复杂。
2、 不完全读取CPUID。由于只是使用CPUID来生成一些硬件特征码,因此,并不需要读取全部的CPUID信息,那么可以屏蔽掉不同核心标识的标志位,比如,我的代码中,屏蔽了ID2来达到此目的。
以下是从网上D下的CPUID读取功能代码,仅供参考:
interface
uses StrUtils,
Windows,
sysUtils;
type
SCPUID = array[ 1 .. 4 ] of Longint;
SVendor = array [ 0 .. 11 ] of char ;
TCPUInfo = class (TObject)
private
FID1: string ;
FID2: string ;
FID3: string ;
FID4: string ;
FAvailable: Boolean;
FVendor: SVendor;
protected
public
constructor create;
published
property ID1: string read FID1;
property ID2: string read FID2;
property ID3: string read FID3;
property ID4: string read FID4;
property Available: Boolean read FAvailable;
end;
const
ID_BIT = $ 200000 ; // EFLAGS ID bit
implementation
function IsCPUID_Available : Boolean; register;
asm
PUSHFD {direct access to flags no possible, only via stack}
POP EAX {flags to EAX}
MOV EDX,EAX {save current flags}
XOR EAX,ID_BIT {not ID bit}
PUSH EAX {onto stack}
POPFD {from stack to flags, with not ID bit}
PUSHFD {back to stack}
POP EAX { get back to EAX}
XOR EAX,EDX {check if ID bit affected}
JZ @exit {no, CPUID not availavle}
MOV AL,True {Result = True}
@exit:
end;
function GetCPUID : SCPUID; assembler; register;
asm
PUSH EBX {Save affected register}
PUSH EDI
MOV EDI,EAX {@Result}
MOV EAX, 1
DW $A20F {CPUID Command}
STOSD {CPUID[ 1 ]}
MOV EAX,EBX
STOSD {CPUID[ 2 ]}
MOV EAX,ECX
STOSD {CPUID[ 3 ]}
MOV EAX,EDX
STOSD {CPUID[ 4 ]}
POP EDI {Restore registers}
POP EBX
end;
function GetCPUVendor : SVendor; assembler; register;
asm
PUSH EBX {Save affected register}
PUSH EDI
MOV EDI,EAX {@Result (TVendor)}
MOV EAX, 0
DW $A20F {CPUID Command}
MOV EAX,EBX
XCHG EBX,ECX {save ECX result}
MOV ECX, 4
@ 1 :
STOSB
SHR EAX, 8
LOOP @ 1
MOV EAX,EDX
MOV ECX, 4
@ 2 :
STOSB
SHR EAX, 8
LOOP @ 2
MOV EAX,EBX
MOV ECX, 4
@ 3 :
STOSB
SHR EAX, 8
LOOP @ 3
POP EDI {Restore registers}
POP EBX
end;
{ TCPUInfo }
constructor TCPUInfo.create;
var
cpuid: Scpuid;
begin
FAvailable : = IsCPUID_Available;
FID1 : = ' 00000000 ' ;
FID2 : = ' 00000000 ' ;
FID3 : = ' 00000000 ' ;
FID4 : = ' 00000000 ' ;
if FAvailable then
begin
cpuid : = getCPUID;
FID1 : = inttohex(cpuid[ 1 ], 8 );
FID2 : = inttohex(cpuid[ 2 ], 8 ); // 在我的机器上,屏蔽这个字段就可以获取稳定的机器码
FID3 : = inttohex(cpuid[ 3 ], 8 );
FID4 : = inttohex(cpuid[ 4 ], 8 );
FVendor : = getCPUVendor;
end;
end;
end.
uses StrUtils,
Windows,
sysUtils;
type
SCPUID = array[ 1 .. 4 ] of Longint;
SVendor = array [ 0 .. 11 ] of char ;
TCPUInfo = class (TObject)
private
FID1: string ;
FID2: string ;
FID3: string ;
FID4: string ;
FAvailable: Boolean;
FVendor: SVendor;
protected
public
constructor create;
published
property ID1: string read FID1;
property ID2: string read FID2;
property ID3: string read FID3;
property ID4: string read FID4;
property Available: Boolean read FAvailable;
end;
const
ID_BIT = $ 200000 ; // EFLAGS ID bit
implementation
function IsCPUID_Available : Boolean; register;
asm
PUSHFD {direct access to flags no possible, only via stack}
POP EAX {flags to EAX}
MOV EDX,EAX {save current flags}
XOR EAX,ID_BIT {not ID bit}
PUSH EAX {onto stack}
POPFD {from stack to flags, with not ID bit}
PUSHFD {back to stack}
POP EAX { get back to EAX}
XOR EAX,EDX {check if ID bit affected}
JZ @exit {no, CPUID not availavle}
MOV AL,True {Result = True}
@exit:
end;
function GetCPUID : SCPUID; assembler; register;
asm
PUSH EBX {Save affected register}
PUSH EDI
MOV EDI,EAX {@Result}
MOV EAX, 1
DW $A20F {CPUID Command}
STOSD {CPUID[ 1 ]}
MOV EAX,EBX
STOSD {CPUID[ 2 ]}
MOV EAX,ECX
STOSD {CPUID[ 3 ]}
MOV EAX,EDX
STOSD {CPUID[ 4 ]}
POP EDI {Restore registers}
POP EBX
end;
function GetCPUVendor : SVendor; assembler; register;
asm
PUSH EBX {Save affected register}
PUSH EDI
MOV EDI,EAX {@Result (TVendor)}
MOV EAX, 0
DW $A20F {CPUID Command}
MOV EAX,EBX
XCHG EBX,ECX {save ECX result}
MOV ECX, 4
@ 1 :
STOSB
SHR EAX, 8
LOOP @ 1
MOV EAX,EDX
MOV ECX, 4
@ 2 :
STOSB
SHR EAX, 8
LOOP @ 2
MOV EAX,EBX
MOV ECX, 4
@ 3 :
STOSB
SHR EAX, 8
LOOP @ 3
POP EDI {Restore registers}
POP EBX
end;
{ TCPUInfo }
constructor TCPUInfo.create;
var
cpuid: Scpuid;
begin
FAvailable : = IsCPUID_Available;
FID1 : = ' 00000000 ' ;
FID2 : = ' 00000000 ' ;
FID3 : = ' 00000000 ' ;
FID4 : = ' 00000000 ' ;
if FAvailable then
begin
cpuid : = getCPUID;
FID1 : = inttohex(cpuid[ 1 ], 8 );
FID2 : = inttohex(cpuid[ 2 ], 8 ); // 在我的机器上,屏蔽这个字段就可以获取稳定的机器码
FID3 : = inttohex(cpuid[ 3 ], 8 );
FID4 : = inttohex(cpuid[ 4 ], 8 );
FVendor : = getCPUVendor;
end;
end;
end.