1

I am running Ubuntu and I don't understand why it's so hard for me to get the number of cores on my system in C! I've tried parsing /proc/cpuinfo with success on my system running Ubuntu but then I tried on another system running arch linux which would fail because the buffer was too small, and I can't seem to figure out how to make it work on both of my systems.

#include <stdio.h>
#include <stdlib.h>

int main() {
  FILE* fp; 
  char *buffer, *tmp; 
  size_t bytes_read, size; 

  if((fp = fopen("/proc/cpuinfo", "r")) == NULL) {
    perror("open failed");
    exit(EXIT_FAILURE); 
  }
  size = 1024;
  if((buffer = malloc(size)) == NULL) {
    perror("malloc failed");
    free(buffer);
    exit(EXIT_FAILURE);
  }
  while(!feof(fp)) {
    bytes_read = fread(buffer, 1, size, fp);
    if(bytes_read == size) {
      size += 128;
      if((tmp = realloc(buffer, size)) == NULL) {
        perror("realloc failed");
        free(buffer);
        exit(EXIT_FAILURE);
      }else {
        buffer = tmp;
      }
    } 
  }
  fclose(fp); 
  if(bytes_read == 0 || bytes_read == size) {
    perror("read failed or buffer isn't big enough.");
    exit(EXIT_FAILURE); 
  }
  printf("%d bytes read out of %d\n", (int)bytes_read,(int) size);
  buffer[bytes_read] = '\0';

  printf("%s", buffer); 
}

This outputs 572 bytes read out of 1152, but it overwrites the buffer whenever I use fread again. And I can't use sysconf(_SC_NPROCESSORS_ONLN); either because it doesn't work on Ubuntu it seems.

Linus
  • 1,516
  • 17
  • 35
  • 3
    Don't do `while (!feof(...))`, it will seldom work as you expect. The reason being that the end-of-file flag isn't set until after you try read from beyond the end of the file the first time. This leads to your loop iterating once to many, without you noticing really. – Some programmer dude Jul 30 '15 at 10:56
  • 3
    Secondly, using `sizeof` on a pointer returns the size *of the pointer* and not what it points to. – Some programmer dude Jul 30 '15 at 10:57
  • @JoachimPileborg But `ftell` doesn't work with `/proc/cpuinfo` since I can't open in binary mode, probably because it thinks I want to execute the file. – Linus Jul 30 '15 at 10:57
  • 1
    Your `free()` after `realloc() == 0` does nothing, the original memory is leaked. – EOF Jul 30 '15 at 10:58
  • 1
    Thirdly, don't reassign the pointer returned by `realloc` to the pointer you reallocate. If `realloc` fail you will loose the original pointer. – Some programmer dude Jul 30 '15 at 10:58
  • I seem to have broken every rule there is about memory management... – Linus Jul 30 '15 at 10:59
  • 1
    Read [the `fread` manual page](http://man7.org/linux/man-pages/man3/fread.3.html) more thoroughly, especially about what it returns. – Some programmer dude Jul 30 '15 at 11:02
  • @JoachimPileborg okay will do. But how will that solve my problem of the buffer being too small? If I can't use `ftell` nor `feof`? See my edits by the way, I fixed a bunch of stuff. – Linus Jul 30 '15 at 11:04
  • If `fread` returns less than requested, *then* check for errors or end of file. If all requested data was read, then reallocate and append to buffer. – Some programmer dude Jul 30 '15 at 11:06
  • @JoachimPileborg right, how do I append to the buffer instead of overwriting it using fread? – Linus Jul 30 '15 at 11:07
  • grep -c '^proc' /proc/cpuinfo – stark Jul 30 '15 at 11:10
  • 2
    Why do you need to read in the whole content to get the number of cores? Simply parse the interested lines. See [this answer for an example to parse /proc/cpuinfo or /proc/stat to get the number of cores](http://stackoverflow.com/a/24127920/1275169). – P.P Jul 30 '15 at 11:10
  • Look at the buffer as an array of chunks, each chunk is X bytes large (in your case it seems to be 128 bytes). Always read the size of X, at position N in your "array". So the first iteration you write at position `0 * X` in the buffer, the second iteration `1 * X`, the third `2 * X` etc. – Some programmer dude Jul 30 '15 at 11:11
  • @EOF, `free()` after `realloc() == 0` can be undefined behaviour. You are not allowed to `free(NULL)` (at least in old `libc` implementations) and the original memory is not always leaked (you can do `b = realloc(a, new_size)`, which leads to no leak. – Luis Colorado Jul 31 '15 at 11:56
  • @LuisColorado: No. `man free(3): [...]If ptr is NULL, no operation is performed.[...]`, `C11 draft standard, Section 7.22.3.3 The free function: [...] If ptr is a null pointer, no action occurs.[...]`. You bloody well *are* allowed to `free(0)` any day, every day. – EOF Jul 31 '15 at 12:22
  • yes, not everybody uses C11 standard... look in K&R if you wan to be actually portable. – Luis Colorado Jul 31 '15 at 12:40
  • @LuisColorado: Oh, look! It's the same in C99. Do you want to go find an example of a C-standard where it is *not allowed*? 'Cause if you don't, I'll just assume you don't know what you are talking about. – EOF Jul 31 '15 at 12:48
  • A more thorough way would be to adapt `lscpu` source code to fit your needs https://github.com/karelzak/util-linux/blob/master/sys-utils/lscpu.c – Fravadona Apr 02 '21 at 14:55

3 Answers3

3

How about using popen(3) to execute cat ^processor /proc/cpuinfo | wc -l to get the count of CPUs and then read the result from the pipe? It's quite easy, and you won't have to maintain complicated code to read and parse the entire file.

Here's an example:

#include <stdio.h>

int ncpus(void) {
    FILE *cmd = popen("grep '^processor' /proc/cpuinfo | wc -l", "r");

    if (cmd == NULL)
        return -1;

    unsigned nprocs;
    size_t n;
    char buff[8];

    if ((n = fread(buff, 1, sizeof(buff)-1, cmd)) <= 0)
        return -1;

    buff[n] = '\0';
    if (sscanf(buff, "%u", &nprocs) != 1)
        return -1;

    pclose(cmd);

    return nprocs;
}

int main(void) {
    int cpus = ncpus();
    if (cpus == -1)
        fprintf(stderr, "Error retrieving number of CPUs\n");
    else
        printf("Number of CPUs: %d\n", cpus);
    return 0;
}

You might want to improve error handling in ncpus() to be a little bit user-friendly (right now, you don't really know what happened if if returns -1).

UPDATE

As mentioned below in the comments, nproc(1) might be a better choice here, at least the command is smaller. It will work either way, we can just replace grep + wc with nproc.

Filipe Gonçalves
  • 20,783
  • 6
  • 53
  • 70
  • that's a cool way of doing it, however is using nproc better? I mean it is certainly less complicated. – Linus Jul 30 '15 at 11:47
  • @Linus Ah yeah, absolutely! You can definitely use `nproc(1)` in place of `grep` + `wc`. I didn't think about it. It should work if you just change the command passed to `popen(3)`, since both commands have the same output. Updated my answer (and confirmed that it works both ways). – Filipe Gonçalves Jul 30 '15 at 11:48
1

Here's a minimal example of how to get physical cores and virtual threads:

#include <stdio.h>

...

FILE *cpu_info = fopen("/proc/cpuinfo", "r");
unsigned int thread_count, core_count;
while (!fscanf(cpu_info, "siblings\t: %u", &thread_count))
  fscanf(cpu_info, "%*[^s]");
while (!fscanf(cpu_info, "cpu cores\t: %u", &core_count))                   
  fscanf(cpu_info, "%*[^c]");                                                                                          
fclose(cpu_info);

Note that you don't need to check for EOF in this example as fscanf will return EOF if reached. This will cause the loop to stop safely.

Also, this example doesn't contain error checking to see if fopen failed. This should be done however you see fit.

This fscanf technique was derived from here: https://stackoverflow.com/a/43483850

0

This is old Question ,But if anyone got the same question. This code also works for bigLITTLE processors.

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include<map>
#include<cstring>
using namespace std;

int main() {
    map <string,int > cpu_count;
    map <string,float > freq_count;
    string line;
    string temp="";
    ifstream finfo("/proc/cpuinfo");
    while(getline(finfo,line)) {
        stringstream str(line);
        string itype;
        string info;
        stringstream str1(line);
        string itype1;
        string info1;
        
        if ( getline( str, itype, ':' ) && getline(str,info) && itype.substr(0,10) == "model name" ) {
            cpu_count[info]++;
            temp=info;  
        }

        if ( getline( str1, itype1, ':' ) && getline(str1,info1) && (itype1.substr(0,7) == "cpu MHz" || itype1.substr(0,8) == "BogoMIPS") ) {
            float freq = stof(info1);
            freq_count[temp]+=freq;
        }
    }

    map<string, int>::iterator it1;
    map<string, float>::iterator it2;
    it2=freq_count.begin();
    for (it1 = cpu_count.begin(); it1 != cpu_count.end(); it1++)
    {
        cout << "CPU Model : "<<it1->first << " | Cores : " << it1->second << " | Average Frequency :  " <<it2->second/it1->second<<endl;
        it2++;
    }
    return 0;
}

Output

CPU Model : Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz | Cores : 12 | Average Frequency : 808.687

aniket dhole
  • 91
  • 1
  • 7