我在基于Intel Core2的Win2k3机器上运行我的NT服务,我需要遍历所有逻辑CPU(进程关联中的所有位)。为此,我调用GetProcessAffinityMask()来检索系统关联掩码,然后依次将进程切换到每个处理器:
DWORD systemMask;
GetProcessAffinityMask( ... &systemMask );
DWORD processorId = 1;
while( systemMask != 0 ) {
SetProcessAffinityMask(... processorId );
Sleep( 1 ); // to be sure that it shifts to that processor
systemMask >>= 1;
processorId <<= 1;
}
在每次迭代中,我调用code from here来检索当前处理器的APIC id。问题是对于不同的处理器,它有时会返回相同的APIC ID。根据文档,系统中的每个处理器必须具有相同的ID。
我试过调试一下 - 检查Windows是否实际改变了亲和力:
while( systemMask != 0 ) {
SetProcessAffinityMask(... processorId );
Sleep( 1 ); // to be sure that it shifts to that processor
DWORD tempAffinity;
GetProcessAffinityMask( ... &tempAffinity );
// run APIC id detection code here
systemMask >>= 1;
processorId <<= 1;
}
它完全返回我期望的亲和力掩码,但APIC ID对于不同的处理器仍然可以是相同的。
对这种奇怪的情况有解释吗?
答案 0 :(得分:2)
您无法使用Windows API调用来确定此。
首先,在使用EAX调用CPUID之后,Intel CPU上的APCID(不确定AMD或其他)最初编码在EBX寄存器的第8位部分(第24-31位),也就是EBX [31:24] = 0x1。
考虑以下带内联汇编的C / C ++代码:
unsigned int cpu_eax;
unsigned int cpu_ebx;
unsigned int cpu_ecx;
unsigned int cpu_edx;
unsigned int apic_id;
__asm
{
mov eax, 0x1
cpuid
mov [cpu_eax], eax
mov [cpu_ebx], ebx
mov [cpu_ecx], ecx
mov [cpu_edx], edx
}
apic_id = ( cpu_ebx & 0xFF000000) >> 24;
GetProcessAffinityMask简单地枚举所有可用内核,CPU内核和每个CPU的超线程内核,并返回这些组合的总和。它没有物理CPU与CPU内核相对于具有超线程能力的内核的概念。至少它不报告它。
答案 1 :(得分:1)
这是因为cpuid不能在MSVC ++内联__asm语句中使用,因为它会破坏寄存器(即编译器在eax中存储了一些变量,你调用了cpuid,它改变了编译器后面的寄存器)。 MSVC ++编译器不等同于GCC's clobber list,因此它不起作用。
你需要使用另一种方法来识别当前正在运行的CPU,虽然我无法想到一个好的...
编辑另外,您为什么关心APIC ID?如果你想要的是在n个处理器上按顺序执行n次相同的代码,你不能只设置亲和度,睡眠,增量亲和力,睡眠等吗?
答案 2 :(得分:1)
你可以将DPC发布到不同的cpu并在DPC程序中进行操作。
答案 3 :(得分:0)
在循环的第一次迭代中,似乎您将关联掩码设置为0. MSDN文档没有明确指出在这种情况下应该是什么行为,但我敢打赌它会让线程在任何地方运行在那种情况下。