CPUの物理CPU数・ソケット数等を取得(32/64bit)・プログラムソースの概要

icon 項目のみ表示/展開表示の切り替え

プログラムソースの概要

cpuid4.cpp

CPUクラスのオブジェクトを割り当て・初期化します。

WinMain

ダイアログボックスを表示します。

DlgProc

ダイアログボックスプロシージャーです。
WM_INITDIALOG
ListViewHeader関数によりリストビューのヘッダーを表示します。
putListView関数によりリストビューのセルに表示します。

WM_COMMAND

IDOK
OKボタンをクリックすると発生するメッセージです。
EndDialog APIによりダイアログボックスを終了させます。

ListViewHeader

リストビューのヘッダーを表示します。
ソケット数に応じて列を増やします。

putListView

リストビューに各ソケット毎に、コア数・スレッド数及びコア内のスレッドに対するWindowsが割り当てた論理CPU番号及びコア番号を表示します。

cpu.h cpu.cpp

CPUIDを管理するクラス及びCPUIDを解析するための共用体を定義しています。
エミュレーション時は、このクラスが保持する値はエミュレーション中の値となります。

CPU_PKG クラス

CPUの物理パッケージを管理します。
DWORD coreMax;
コア数
DWORD threadMax;
スレッド数
CACHE_CODE cache[8];
キャッシュの情報を保存しています。
DWORD cache_max;
キャッシュ情報の個数
int* threadvec;
CPUのスレッド番号を配列の添え字 → Windows上の論理CPU番号
int* corevec;
CPUのスレッド番号→コア番号
cpuid?????????_tbl;
cpuidの実行結果のテーブル(エミュレーション時は継承側のクラスで定義する)

REG32* cpuid_tbl;//     eax=0~指定されるcpuid
REG32* cpuid80000000_tbl;//     eax=0x80000000~指定されるcpuid
REG32* cpuid2_tbl;//    eax=2で指定されるcpuid
REG32* cpuid4_tbl;//    eax=4 ,ecx指定されるcpuid
REG32* cpuid7_tbl;//    eax=7 ,ecx指定されるcpuid
REG32* cpuid11_tbl;//   eax=11,ecx指定されるcpuid
REG32* cpuid13_tbl;//   eax=13,ecx指定されるcpuid
DWORD cpuid????????_max;
cpuid????????_tblの要素数

DWORD cpuid_max;        //      cpuid_tblの要素数
DWORD cpuid80000000_max;//      cpuid80000000_tblの要素数
DWORD cpuid2_max;       //      cpuid2_tblの要素数
DWORD cpuid4_max;       //      cpuid4_tblの要素数
DWORD cpuid7_max;       //      cpuid7_tblの要素数
DWORD cpuid11_max;      //      cpuid11_tblの要素数
DWORD cpuid13_max;      //      cpuid13_tblの要素数
TCHAR name;
CPUの名前
void cache_set(void);
cpuid EAX=2とcpuid EAX=4によりキャッシュ諸元を設定cache[n]に設定します。
基本的にはcpuid EAX=4の情報のみで行けますが、Pentium4のトレスキャッシュのみcpuid EAX=2にしか情報がありませんので、cpuid EAX=2でキャッシュ情報を一括して取得後、cpuid EAX=4で重複をチェックしながらcache[n]に登録します。
CACHE_CODE* cpuid2_cache_find(DWORD code);
cpuid EAX=2の実行結果の各1byteを引数に渡して呼び出すと該当するキャッシュサイズ等を示す構造体のポインタを返します。
void cpuid2_cache_set(DWORD c);
cpuid EAX=2の実行結果の各1byteを引数に渡して呼び出すと該当するキャッシュサイズ等を示す構造体を検索して該当するものがあればcahche[n]に登録し、cache_maxを1加算します。
void getCPUID(void);
現システムの指定される物理パッケージのCPUIDを取得します。
cpuid EAX=2 EAX=11は、コア及びスレッドにより異なったCPUIDの結果を返すので、SetThreadAffinityMask APIにより実行時のCPUを固定してから__cpuid及び__cpuidexを実行しています。
物理パッケージ内の全スレッドに対して順番に実行します。
実行結果はcpuid?????????_tbl,cpuid????????_maxに保存されます。
bool emuIsX2apic(void);
CPUがx2apicをサポートしているかをcpuid?????????_tblによって判断しサポートしている場合trueを返します。直接__cpuid及び__cpuidexを呼び出していないのでエミュレーション時を対象とします。
基本的には、cpuidでeax=11をサポートしていればx2apicをサポートしています。ただし、Core2Duoの一部は、eax=11をサポートしているが、返す値は無効な値なので、x2apicをサポートしていない。
bool isX2apic(void);
CPUがx2apicをサポートしているかを直接__cpuid及び__cpuidexを呼び出しすことにより判断しサポートしている場合trueを返します。
DWORD getTopologyBit(DWORD* CorePlus_Mask_Width, DWORD* SMT_Mask_Width);
CPUトポロジーのビット位置を直接__cpuidexを呼び出しすことにより取得します。
x2apicが使える場合はx2apic上の位置とx2apicの値を返します。
x2apicが使えない場合はapic上の位置とapicの値を返します。
CPUトポロジーのビット位置とはスレッドのビット幅(CorePlus_Mask_Width)とコアのビット幅(SMT_Mask_Width)を意味します。
例えばCorePlus_Mask_Width=3,SMT_Mask_Width=1で返されたapicまたはx2apicの値が0x1fの場合、
SMT_Mask_Widthが1のなのでビット幅1となり、スレッド番号は0ビット目に含まれ SMT=1
CorePlus_Mask_Widthが3のなので、ビット幅は(CorePlus_Mask_Width-SMT_Mask_Width=2)となり、コア番号は1ビット目から3ビット目に含まれ3となります。
パッケージ番号は、CorePlus_Mask_Widthの上位ビットなので、4ビット目から上位ビットに向かって含まれ、1となります。
void SetTopology(DWORD* apic, DWORD pkg, DWORD core, DWORD smt, DWORD CorePlus_Mask_Width, DWORD SMT_Mask_Width);
指定されたCPUトポロジーのビット位置にパッケージ番号及びコア番号及びスレッド番号を格納します。
void getEmuAPICBit(DWORD* CorePlus_Mask_Width, DWORD* SMT_Mask_Width);
apicよりCPUトポロジーのビット位置をcpuid?????????_tblより取得します。
bool getEmuX2apicBit(DWORD* CorePlus_Mask_Width, DWORD* SMT_Mask_Width);
x2apicよりCPUトポロジーのビット位置をcpuid?????????_tblより取得します。
CPU拡張トポロジーをサポートしているときはtrueを返す
void cpuid(int thread, int* info, int eax);
cpuid?????????_tblよりcpuidをエミュレーションします。
void cpuidex(int thread, int* info, int eax, int ecx);
cpuid?????????_tblよりcpuidexをエミュレーションします。
TCHAR* ext_inst_str(TCHAR* buf, int sz);
サポートされている拡張命令を文字列化します。
例えばMMX,SSEをサポートしているCPUの場合、文字列MMX SSEをbufにコピーします。
void cpuid_summary(TCHAR* str,int sz);
CPUIDの結果の概要をカンマで区切って文字列に出力します。 例えばPentium 4の場合、以下のような文字列が作成されます。
Intel(R) Pentium(R) 4 CPU 3.20GHz,1,2,Trace Cache 12kμOPs 8way*1個,16kByte*1個 8way,2048kByte*1個 8way,,f,6,2,f,6,MMX SSE SSE2 SSE3 EM64T NX/XD-bit 
void cpuid????????_info_put(FILE* fp);

void cache_put(FILE* fp);       //      キャッシュ情報の出力
void DrawOwn(HDC hdc, int sx, int sy, int cpuNum);      // 1個の物理CPUを描画
void Draw(HDC hdc, int sx, int sy, int* ex, int* ey);   // 1個のパッケージを描画
void cpuid2_info_put(FILE* fp); //      cpuid EAX=2の実行結果を出力
void cpuid4_info_put(FILE *fp); //      cpuid EAX=4の実行結果を出力
void cpuid7_info_put(FILE* fp); //      cpuid EAX=7の実行結果を出力
void cpuid10_info_put(FILE *fp);        //      cpuid EAX=10の実行結果を出力
void cpuid11_info_put(FILE *fp);        //      cpuid EAX=11の実行結果を出力
void cpuid11_small_put(FILE* fp);       //      cpuid EAX=11の実行結果を出力(簡易)
void cpuid80000008_info_put(FILE *fp);  //      cpuid EAX=0x80000008の実行結果を出力
void cpuid_info_put(FILE *fp);  //      cpuidの実行結果を全部出力
void cpuid_cpp_put(FILE* fp, TCHAR* fname);     //      物理パッケージのCPUIDをC++のクラスとしてソースをファイルに出力
void cpuid_put(FILE* fp);       //      取得されたcpuidをファイルに出力
void cpuid_put_ListView(HWND hList);    //      cpuidをリストビューに出力
x2APIC
各スレッド数のパッケージ・コア・スレッド番号を保存しています。
APIC
各スレッド数のパッケージ・コア・スレッド番号を保存しています。

CPU クラス

CPUのパッケージを複数個まとめて管理します。
DWORD lCpuMax;
システムの論理CPU数を返します。
void cpuid_cpp_put(void);
物理パッケージのCPUIDをC++のクラスとしてソースをファイルに出力
void cpuid_put(FILE* fpv);
取得されたcpuidをファイルに出力
void cpuid_info_put(FILE* fpv);
cpuidの実行結果を全部出力
CPU();
エミュレーションしない場合のコンストラクタ
CPU(int package);
エミュレーションする場合のコンストラクタ 物理パッケージ数(ソケット数)を指定
TCHAR* getCpuName(void);
CPUの名称を返す。
CPU_PKG** cpu_pkg;
物理パッケージ(*CPU_PKG)を保存
DWORD pkgMax;
物理パッケージ数
DWORD* smt, *core, *pkg;
Windowsの論理CPU番号を添え字としスレッド番号、コア番号、パッケージ番号を保存
UINT64 mask;
CPUIDをエミュレーションしている時、実CPU数とエミュレーションCPU数が一致しない時があるので、CPUのプロセッサ・アフィニティ・マスクを仮想化します。仮想化した値を保持しており、emuSetThreadAffinityMasメンバ関数の引数が保存されます。
UINT64 sysmask;
エミュレーションしているCPU数全部にスレッドを割り当てるCPUのプロセッサ・アフィニティ・マスク値を保存しています。
int* emu_cpu_num;
仮想CPU番号を添え字としWindowsの論理CPU番号を保存しています。
emuSetThreadAffinityMasメンバ関数により割り当てCPUを変更すると実CPUへの割り当ても変更します。
void make_cpuid_para(int* cpun, int* pkgn, int* threadn);
仮想CPUのプロセッサ・アフィニティ・マスク値より一番わかりCPU番号等を取得します。
void alloc_cpu(int* v, int vcpumax);
仮想CPU番号を添え字としWindowsの論理CPU番号を格納する配列を初期化します。
仮想論理CPU<=実論理CPU/2の場合、ハイパースレッドのCPUを考慮して実論理CPUを2個飛ばしに割り振ります。

仮想論理CPU 2個
実論理CPU 8個
emu_cpu_num[0] -> 0
emu_cpu_num[1] -> 2
仮想論理CPU>実論理CPU/2の場合、実論理CPUを1ずつ加算して割り振ります。

仮想論理CPU 4個
実論理CPU 2個
emu_cpu_num[0] -> 0
emu_cpu_num[1] -> 1
emu_cpu_num[2] -> 0
emu_cpu_num[3] -> 1
void init(void);
エミュレーション時にCPUトポロジーよりパッケージ・コア・スレッドを取得します。
本関数を呼び出す前に、まず本オブジェクトの作成のコンストラクタに物理パッケージ数を指定します。CPU_PKGから派生されたオブジェクトを作成し、addメンバ関数でCPU_PKGから派生されたクラスを追加する必要があります。その後、parseメンバー関数を呼び出すと、エミュレーションが使用可能となりemucpuid,emucpuidex,emuGetSystemInfo,emuSetThreadAffinityMaskメンバ関数が使用できます。
void getCPUID(void);
現システムから各cpu_pkgオブジェクトのgetCPUIDを呼び出し、CPUIDの実行結果を抽出します。
int bit_num(UINT64 bit);
引数に含まれる1のビット数を返します。
DWORD_PTR emuSetThreadAffinityMask(HANDLE h, UINT64 vmask);
実行CPUを固定します。指定するCPUのプロセッサ・アフィニティ・マスクの値は仮想化したものを指定します。詳しくはUINT64 sysmask;を参照してください。
BOOL emuGetProcessAffinityMask(HANDLE h, UINT64* vmask, UINT64* vsysmask);
仮想化されたCPUのプロセッサ・アフィニティ・マスクの値を返します。
void emuGetSystemInfo(SYSTEM_INFO* sys);
GetSystemInfo APIを呼び出し、戻り値のうち論理CPU数を仮想化された論理CPU数に書き換えて返します。
void cpuid(int* info, int eax);
仮想化されたCPUのプロセッサ・アフィニティ・マスクの値より実行中の仮想論理CPU番号を算定し、取得されたcpuidによりエミュレーションします。
void cpuidex(int* info, int eax, int ecx);
仮想化されたCPUのプロセッサ・アフィニティ・マスクの値より実行中の仮想論理CPU番号を算定し、取得されたcpuidによりエミュレーションします。
void get(void);
現システムのCPUトポロジー等を解析します。
void parse(void);
現システムまたはエミュレーションされた結果より詳細のCPUトポロジー等を解析します。
void add(int package, CPU_PKG* cpu);
エミュレーションするCPUを追加します。
int getPackageMax(void);
物理パッケージ数(ソケット)を取得