Get CPU info

This is a discussion on Get CPU info within the C Programming forums, part of the General Programming Boards category; Hello... I am trying to get CPU info on Linux. I need to get how many cpu\cores the CPU have. ...

  1. #1
    Registered User
    Join Date
    Oct 2007
    Posts
    6

    Get CPU info

    Hello...

    I am trying to get CPU info on Linux. I need to get how many cpu\cores the CPU have. It is like "cat /proc/cpuinfo" but I am looking for a syscall or something like this. Anybody know?

    Thanks.
    Elvio

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    As far as I know [and I have dealt with those sort of things], the way you _do_ get that info from the system is by reading /proc/cpuinfo - you can read it from inside an application by using fopen() and fgets() for example.

    This is done this way, because it is much more flexible with new architectures than to supply the information as a struct or other type of "data back from the OS".

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Mar 2008
    Posts
    15
    A much better solution to this would be using the CPUID instruction. Almost every CPU since the 486 support it (so all Hyperthreading/Multi-core Intel or AMD cpus). The following solution will only work for Intel/AMD cpus.
    Here's the code to get the number of logical and physical CPUs for ONE physical processor package:

    (Code is written for GCC on Unix-like systems, but should be easy to port it to Windows if needed)

    Code:
    #include <stdio.h>
    #include <math.h>
    
    void cpuid(int *a, int *b, int *c, int *d)
    {
      int keeper;
      __asm__ __volatile__ (" mov &#37;5, %%eax;"
                            " mov %6, %%ecx;"
                            " mov %%ebx, %0;"
                            " cpuid;"
                            " mov %%eax, %1;"
                            " mov %%ebx, %2;"
                            " mov %%ecx, %3;"
                            " mov %%edx, %4;"
                            " mov %7, %%ebx"
             /* Output */ : "=r" (keeper), "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
             /* Input  */ : "a" (*a), "c" (*c), "r" (keeper) );
    }
    
    
    int bit_check(int *x, int bit)
    {
       if (((*x >> bit) & 0x01) == 0)
          return 0;      /* bit ain't set */
       else
          return 1;      /* bit is set */
    }
    
    
    int read_val(int *reg, int startbit, int endbit)
    {
      int i,val=0;
    
      for (i=0; i<=(endbit-startbit); i++)
      {
        if ( bit_check(reg,startbit+i) == 1 ) val=val+pow(2,i);
      }
      return val;
    }
    
    
    int main()
    {
      int a=0,b=0,c=0,d=0;
      int maxinstr=0,maxextinstr=0;
      int vendor=0;
      int cores=1,threads=1;
    
      /* MAX CPUID INSTRUCTION LEVELS */
      a=0x80000000; cpuid(&a,&b,&c,&d);
      maxextinstr=a;
      a=0; cpuid(&a,&b,&c,&d);
      maxinstr=a;
      vendor=b; /* Registers EBX, EDX and ECX hold the vendorstring now, we compare EBX to see if it's 'Genu' or 'Auth'
                   because the vendorstring for Intel is 'GenuineIntel' and for AMD it's 'AuthenticAMD' */
    
      /* CPU threads (logical CPUs) for Intel and AMD */
      if ( maxinstr >= 1 )
      {
        a=1; cpuid(&a,&b,&c,&d);
        threads=read_val(&b,16,23);
      }
    
      if ( vendor == 0x756E6547 ) /* Cores for Intel */
      {
        if ( maxinstr >= 4 )
        {
          a=4; c=0; cpuid(&a,&b,&c,&d);
          cores=read_val(&a,26,31)+1;
        }
      }
      else if ( vendor == 0x68747541 ) /* Cores for AMD */
      {
        if ( maxextinstr >= 0x80000008 )
        {
          a=0x80000008; cpuid(&a,&b,&c,&d);
          cores=read_val(&c,0,7)+1;
        }
      }
    
      if ( threads >= 2 )
      {
        if ( threads > cores ) printf("Hyper-Threading CPU (%i threads, %i core(s))\n",threads,cores);
        else                   printf("Multi-core CPU (%i threads, %i cores)\n",threads,cores);
      }
      else printf("Single-core CPU without hyper-threading (1 thread, 1 core)\n");
    
      return 0;
    }
    For details on what I'm doing here see http://en.wikipedia.org/wiki/CPUID or http://download.intel.com/design/pro...s/24161832.pdf for Intel-specific information.

    Have fun,

    Peter
    Last edited by pgzh; 03-21-2008 at 12:39 PM.

  4. #4
    Registered User
    Join Date
    Mar 2008
    Posts
    6
    Hello , I have read your post on forum and it was helpful
    however i cant understand some lines of volatile asm.
    I googled and read http://www.ibm.com/developerworks/library/l-ia.html
    However still no bulb in my head =)

    Code:
    mov %5, %%eax 
    mov %6, %%ecx 
    " mov %7, %%ebx"
    What are %5 , %6, %7 ? Cant resolve it.
    But i can sense that

    Code:
                            " mov %%eax, %1;"
                            " mov %%ebx, %2;"
                            " mov %%ecx, %3;"
                            " mov %%edx, %4;"
    %1 is *a , %2 is *b and so on so forth.

    Plus ,
    Why did u define a "keeper" named variable and used it in output operands if youre not gonna send it out of the "void cpuid" function ?

  5. #5
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,248
    Quote Originally Posted by pgzh View Post
    A much better solution to this would be using the CPUID instruction. Almost every CPU since the 486 support it (so all Hyperthreading/Multi-core Intel or AMD cpus).
    I'm not sure that solution is better. CPUID does give you certain information, but /proc/cpuinfo gives you even more than just the CPUID. For instance, the CPU megahertz, which you can't get from CPUID.

    So it really depends on what information you're looking for. And breaking compatibility with very old CPUs, while not the worst thing in the world you could do, is not really necessary.

  6. #6
    Registered User
    Join Date
    Mar 2008
    Posts
    15
    Quote Originally Posted by lastsurvivor View Post
    But i can sense that

    Code:
                            " mov %%eax, %1;"
                            " mov %%ebx, %2;"
                            " mov %%ecx, %3;"
                            " mov %%edx, %4;"
    %1 is *a , %2 is *b and so on so forth.
    The inline assembly for the GCC can't work with variables directly, so you have to use %0 for the first variable you will be using, %1 for the second and so on.
    I guess that's what you already found out.
    If you have Registers you're writing the value of a value to, those are input operations. Writing the register value to a variable is a output operation.
    Since you're going to name the output variables first and the input ones after, input variable numbering will start at %0. Output variable numbering starts at the max input variable +1, so if we've got 5 input variables (max %4 since 0 counts), input variables will be named %5 and up.
    If you've got no output variables at all, input variables will start at %0 of course

    Quote Originally Posted by lastsurvivor View Post
    Plus ,
    Why did u define a "keeper" named variable and used it in output operands if youre not gonna send it out of the "void cpuid" function ?
    That "keeper" variable keeps the value of register EBX before executing the CPUID instruction and is used to write that value back after the EAX, EBX, ECX and EDX register values have been saved to *a,*b,*c and *d.
    If you modify the EBX register (or "clobber" it as it's called in assembly) and compile code with the -fpic option of GCC you'll get an error, because the EBX register is supposed to hold some pretty important data. (I think it's about memory adresses)
    Details on PIC (Position Independent Code) can be found at http://en.wikipedia.org/wiki/Position_independent_code .
    It's particularly interesting for shared libraries or making code invulnerable against some exploits because the code's memory adresses are "hidden".
    So I added this in case some guys running hardened gentoo (which uses -fpic AFAIK) try to compile this with their default CFLAGS. Dunno if it's the perfect workaround, but at least I know it works.


    If you're interested in the CPUID instruction, I can send you a small tool for Linux I'm developing that detects the CPU, the microarchitecture, codename, core stepping and has more sophisticated cache recognition than /proc/cpuinfo for multicore CPUs.

    Peter

  7. #7
    Registered User
    Join Date
    Mar 2008
    Posts
    6
    Thank you , that makes perfect sense now.

  8. #8
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,248
    Quote Originally Posted by pgzh View Post
    That "keeper" variable keeps the value of register EBX before executing the CPUID instruction and is used to write that value back after the EAX, EBX, ECX and EDX register values have been saved to *a,*b,*c and *d.
    If you modify the EBX register (or "clobber" it as it's called in assembly) and compile code with the -fpic option of GCC you'll get an error, because the EBX register is supposed to hold some pretty important data. (I think it's about memory adresses)
    This is all unnecessary. GCC will automatically save and restore any registers it needs to. Just make sure that EBX is listed in the clobber list or the output list.

  9. #9
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,676
    >> I need to get how many cpu\cores the CPU have. ... but I am looking for a syscall ...
    There's "sysconf(_SC_NPROCESSORS_CONF)", but I would ask what you want to do with the information. If it's for scheduling/binding threads to a particular core/cpu - then there's a difference between the number of cpu's and what cpu's you can run on. For the later, there's sched_getaffinity/sched_setaffinity. This is also worth reading for later case: http://groups.google.com/group/comp....e0001a546c732b
    Keep in mind that sched_[g|s]etaffinity() is "linux-only".

    gg

  10. #10
    Registered User
    Join Date
    Mar 2008
    Posts
    15
    Quote Originally Posted by brewbuck View Post
    This is all unnecessary. GCC will automatically save and restore any registers it needs to. Just make sure that EBX is listed in the clobber list or the output list.
    That's strange, because I did this after a tester of my tool reported the following error compiling my program:
    Code:
    error: can't find a register in class ‘BREG’ while reloading ‘asm’
    With Google I found the following: http://sam.zoy.org/blog/2007-04-13-s...d-pic-mix-well

    So I decided to save the EBX register in my "keeper" variable and AFAIK it worked for that guy...

    My original code looks like this, maybe you can tell me if there's another culprit that could lead to the reported problem:
    Code:
    void cpuid(int *a, int *b, int *c, int *d)
    {
      __asm__ __volatile__ (" mov &#37;4, %%eax;"
                            " mov %5, %%ecx;"
                            " cpuid;"
                            " mov %%eax, %0;"
                            " mov %%ebx, %1;"
                            " mov %%ecx, %2;"
                            " mov %%edx, %3;"
             /* Output */ : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
             /* Input  */ : "a" (*a), "c" (*c) );
    }
    Peter
    Last edited by pgzh; 03-27-2008 at 12:50 PM.

  11. #11
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,248
    Quote Originally Posted by pgzh View Post
    That's strange, because I did this after a tester of my tool reported the following error compiling my program:
    Code:
    error: can't find a register in class ‘BREG’ while reloading ‘asm’
    That's the code generator in GCC puking out. Try fiddling with the optimization settings. It's a bug in GCC. If you're hitting it, then you should probably save and restore EBX manually, like you're doing now. I hope they fix it -- it happens with other register classes sometimes, too.

    The cheap solution, though, is to just "pusha" at the beginning and "popa" at the end. I doubt efficiency really matters in this function.

    EDIT: I'd actually rewrite that asm to look like this:

    Code:
    __asm__ __volatile__ ("cpuid"
      : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
      : "a" (*a), "c" (*c)
      : "ebx" );
    That might work better, no guarantee..
    Last edited by brewbuck; 03-27-2008 at 01:01 PM.

  12. #12
    Nub SWE
    Join Date
    Mar 2008
    Location
    Dallas, TX
    Posts
    133
    Code:
             /* Output */ : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
             /* Input  */ : "a" (*a), "c" (*c) );
    I'm not -too- familiar with assembly code or what you're doing here, but it looks like you use r for the first time here, instead of b. All of the other variables seem to correspond to the pointer of the same name. I highlighted it in red above.

    In case I wasn't clear, I'm fully aware I'm possibly being an idiot here. Just wanted to make sure you knew this was there and that it was supposed to be that way.

  13. #13
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,248
    Quote Originally Posted by JDGATX View Post
    Code:
             /* Output */ : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
             /* Input  */ : "a" (*a), "c" (*c) );
    I'm not -too- familiar with assembly code or what you're doing here, but it looks like you use r for the first time here, instead of b. All of the other variables seem to correspond to the pointer of the same name. I highlighted it in red above.

    In case I wasn't clear, I'm fully aware I'm possibly being an idiot here. Just wanted to make sure you knew this was there and that it was supposed to be that way.
    It's basically telling the compiler, "use the register indicated by asm param %1", in this case, the param of line " mov %%ebx, %1;" In other words, load %ebx into any available register.

  14. #14
    Nub SWE
    Join Date
    Mar 2008
    Location
    Dallas, TX
    Posts
    133
    Ah, alright! Makes sense.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. questions on multiple thread programming
    By lehe in forum C Programming
    Replies: 11
    Last Post: 03-27-2009, 07:44 AM
  2. Upgrading my old CPU (for another old one!)
    By foxman in forum Tech Board
    Replies: 16
    Last Post: 01-11-2008, 04:41 PM
  3. Can you still view the bios screen with a bad CPU?
    By HyperCreep in forum Tech Board
    Replies: 4
    Last Post: 12-31-2006, 05:57 PM
  4. Question about getting an info class from another Form
    By Joelito in forum C# Programming
    Replies: 0
    Last Post: 10-16-2006, 01:02 PM
  5. Help doing an e-mail program in c...
    By Tyler_Durden in forum C Programming
    Replies: 88
    Last Post: 01-02-2005, 02:12 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21