用于检测APIC id的代码为不同的逻辑处理器返回相同的ID

时间:2009-09-14 11:45:08

标签: c++ windows assembly multicore affinity

我在基于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对于不同的处理器仍然可以是相同的。

对这种奇怪的情况有解释吗?

4 个答案:

答案 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文档没有明确指出在这种情况下应该是什么行为,但我敢打赌它会让线程在任何地方运行在那种情况下。