C和内联asm错误



我正在开发一个Linux设备驱动程序,在那里我遇到了一个烦人的bug,我已经将其简化为下面的userland代码。

目的是通过cpuid指令读取处理器中的内核数

第三阶段似乎只产生了堆芯转储,我对此有什么解释?

输出:

Phase 1: FYI Proc=0x1ac1010
Phase 2: CPU count=8
Segmentation fault (core dumped)

此代码是为Linux 4.0.7-2 64位编写的,使用gcc 5.1.0版编译

#include <stdio.h>
#include <stdlib.h>
typedef struct {
    unsigned long CPU_count;
} PROC;
PROC *Proc=NULL;
unsigned long CPU_count;
unsigned long CPU_Count(void)
{
    unsigned long c=0;
    __asm__ volatile
    (
        "movq   $0x4, %%rax     nt"
        "xorq   %%rcx, %%rcx    nt"
        "cpuid                  nt"
        "shr    $26, %%rax      nt"
        "and    $0x3f, %%rax    nt"
        "inc    %%rax           nt"
        "movq   %%rax, %0"
        : "=m" (c)
        :
        : "rax", "memory"
    );
    return(c);
}
int main(int argc, char *argv[])
{
    if((Proc=malloc(sizeof(PROC))) != NULL)
    {
        printf("Phase 1: FYI Proc=%pn", Proc);
        CPU_count=CPU_Count();
        printf("Phase 2: CPU count=%lun", CPU_count);
        Proc->CPU_count=CPU_Count();
        printf("Phase 3: CPU count=%lun", Proc->CPU_count);
        free(Proc);
        return(0);
    }
    else
        return(-1);
}

正如Carl Norum所提到的,由于您是asm语句,所以它并没有完全描述它对机器状态的影响——可能发生的情况是您正在破坏GCC用于存储某些值的寄存器。比如说Proc指针。

解决方案是定义汇编指令的所有输入和输出,以及它所破坏的任何其他内容。在这种情况下,在内联汇编中尽可能少地执行操作也是一个好主意,而将其他一切都留给编译器。在您的示例中,只有cpuid指令需要在内联程序集中。所以试试这个:

void
cpuid(unsigned value, unsigned leaf,
      unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx) {
    asm volatile("cpuid"
             : "=a" (*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
             : "a" (value), "c" (leaf));
}
unsigned long int CPU_Count(void)
{
    unsigned eax, ebx, ecx, edx;
    cpuid(4, 0, &eax, &ebx, &ecx, &edx);
    return ((eax >> 26) & 0x3F) + 1;
}

请注意,确定CPU核心数量的方法不能可靠地工作。在我的4核CPU上,你的程序说我有8核。这是因为您正在提取的字段提供了最大APIC id,而不是实际的核心数量。英特尔的网络论坛上有一个帖子讨论了这个问题。

最新更新