如何在C中的任何linux发行版上解析/proc/cpuinfo

如何在C中的任何linux发行版上解析/proc/cpuinfo,c,linux,ubuntu,fread,cpu-cores,C,Linux,Ubuntu,Fread,Cpu Cores,我正在运行Ubuntu,我不明白为什么我很难用C语言获得系统的内核数!我在运行Ubuntu的系统上成功地解析了/proc/cpuinfo,但后来我在另一个运行arch linux的系统上进行了尝试,结果失败了,因为缓冲区太小,而且我似乎不知道如何使它在我的两个系统上都能工作 #include <stdio.h> #include <stdlib.h> int main() { FILE* fp; char *buffer, *tmp; size_t by

我正在运行Ubuntu,我不明白为什么我很难用C语言获得系统的内核数!我在运行Ubuntu的系统上成功地解析了/proc/cpuinfo,但后来我在另一个运行arch linux的系统上进行了尝试,结果失败了,因为缓冲区太小,而且我似乎不知道如何使它在我的两个系统上都能工作

#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); 
}
这将输出从1152中读取的572字节,但每当我再次使用fread时,它将覆盖缓冲区。我不能使用sysconf\u SC\u NPROCESSORS\u ONLN;或者是因为它在Ubuntu上似乎不起作用。

使用popen3执行cat^processor/proc/cpuinfo|wc-l获取CPU的计数,然后从管道读取结果如何?这非常简单,并且您不必维护复杂的代码来读取和解析整个文件

下面是一个例子:

#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;
}
现在,您可能希望改进ncpus中的错误处理,使其对用户友好一点,如果返回-1,您不知道会发生什么

更新

正如下面评论中提到的,nproc1可能是一个更好的选择,至少命令更小。无论哪种方式都可以,我们可以用nproc替换grep+wc。

使用popen3执行cat^processor/proc/cpuinfo | wc-l来获取CPU计数,然后从管道读取结果如何?这非常简单,并且您不必维护复杂的代码来读取和解析整个文件

下面是一个例子:

#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;
}
现在,您可能希望改进ncpus中的错误处理,使其对用户友好一点,如果返回-1,您不知道会发生什么

更新


正如下面评论中提到的,nproc1可能是一个更好的选择,至少命令更小。无论哪种方式都可以,我们可以用nproc代替grep+wc。

这是一个老问题,但如果有人问同样的问题。这段代码也适用于bigLITTLE处理器

#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;
}
输出

CPU型号:IntelR CoreTM i7-8750H CPU@2.20GHz |内核:12 |平均频率:808.687


这是个老问题,但如果有人问同样的问题。这段代码也适用于bigLITTLE处理器

#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;
}
输出

CPU型号:IntelR CoreTM i7-8750H CPU@2.20GHz |内核:12 |平均频率:808.687


下面是如何获取物理内核和虚拟线程的一个简单示例:

#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);
注意,在本例中,您不需要检查EOF,因为fscanf将在到达时返回EOF。这将导致回路安全停止

此外,该示例不包含错误检查,以查看fopen是否失败。这应该按照你认为合适的方式进行


此fscanf技术源于此:

以下是如何获取物理内核和虚拟线程的一个简单示例:

#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);
注意,在本例中,您不需要检查EOF,因为fscanf将在到达时返回EOF。这将导致回路安全停止

此外,该示例不包含错误检查,以查看fopen是否失败。这应该按照你认为合适的方式进行


此fscanf技术源于此:

不要在此时执行!当然,它很少能像你想象的那样工作。原因是直到您第一次尝试从文件末尾以外的位置读取后,才会设置文件结尾标志。这会导致循环从一次迭代到多次,而您没有真正注意到。其次,对指针使用sizeof返回指针的大小,而不是指针指向的内容。realloc==0后的空闲时间没有任何作用,原始内存会泄漏。第三,不要将realloc返回的指针重新分配给重新分配的指针。如果realloc失败,您将丢失原始指针。为什么您需要阅读全部内容才能获得内核数?只需解析感兴趣的行。看,不要这样做!当然,它很少能像你想象的那样工作。原因是直到您第一次尝试从文件末尾以外的位置读取后,才会设置文件结尾标志。这会导致循环从一次迭代到多次,而您没有真正注意到。其次,对指针使用sizeof返回指针的大小,而不是指针指向的内容。realloc==0后的空闲时间没有任何作用,原始内存会泄漏。第三,不要将realloc返回的指针重新分配给重新分配的指针。如果realloc失败,您将丢失原始指针。为什么您需要阅读全部内容才能获得内核数?只需解析感兴趣的行。看,这是一种很酷的方式,但是使用nproc更好吗?“我的意思是,它当然不那么复杂了。”莱纳斯:啊,是的,绝对是!你完全可以用nproc1代替grep+wc。我没想过。如果您只是更改传递给popen3的命令,它应该可以工作,因为这两个命令具有相同的输出。更新了我的答案并确认它是双向的。这是一个很酷的方法,但是使用nproc更好吗?“我的意思是,它当然不那么复杂了。”莱纳斯:啊,是的,绝对是!你一定能找到我们
e nproc1代替grep+wc。我没想过。如果您只是更改传递给popen3的命令,它应该可以工作,因为这两个命令具有相同的输出。更新了我的答案,确认它是双向的。这是一个C++答案,这是一个C问题。这是一个C++答案,这是一个C问题。