Linux C语言获取CPU物理核心数 (非逻辑核心)
2025-03-22 01:12:29
Linux下C语言获取物理核心数
有时候,我们需要获取CPU的物理核心数量,而不是逻辑核心数。sysconf(_SC_NPROCESSORS_CONF)
获取到的是逻辑核心数量, 包括了超线程。比如,我的i3处理器,这个函数会返回4,但实际上只有2个物理核心(双核四线程)。怎么用C语言获取物理核心的数量呢?
为什么 sysconf
不行?
sysconf(_SC_NPROCESSORS_CONF)
和 sysconf(_SC_NPROCESSORS_ONLN)
函数获取的是系统配置的处理器数量和当前在线的处理器数量,这些数量包括了超线程带来的逻辑核心。超线程技术让一个物理核心模拟出两个逻辑核心,提高了并行处理能力,但它们共享物理核心的资源。 这两个函数没办法区分物理核心和逻辑核心。
解决方法
要获得物理核心数量,我们需要解析 /proc/cpuinfo
文件或者利用 sysfs
文件系统。下面介绍几种方法:
方法一:解析 /proc/cpuinfo
/proc/cpuinfo
文件包含了CPU的详细信息,我们可以通过解析这个文件来获取物理核心数量。关键是找到 physical id
和 core id
这两个字段。每个物理核心都有一个唯一的 physical id
,同一个物理核心内的所有逻辑核心共享相同的 physical id
和 core id
。
原理:
- 逐行读取
/proc/cpuinfo
。 - 查找
physical id
和core id
字段。 - 使用一个数据结构(如数组或哈希表)来记录每个 (physical id, core id) 对。
- 不重复的 (physical id, core id) 对的数量就是物理核心数量。
代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int get_physical_cores() {
FILE *fp;
char line[256];
char physical_id[32] = {0};
char core_id[32] = {0};
char last_physical_id[32] = {0};
int physical_cores = 0;
int core_seen[1024] = {0}; //假设最多有1024个core_id
int index=0;
fp = fopen("/proc/cpuinfo", "r");
if (fp == NULL) {
perror("无法打开 /proc/cpuinfo");
return -1;
}
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "physical id\t: %s", physical_id) == 1) {
strcpy(last_physical_id,physical_id);
}
if (sscanf(line, "core id\t: %s", core_id) == 1) {
index = atoi(last_physical_id) * 512 + atoi(core_id); //随便给个数组大小就行
if (core_seen[index] == 0) {
core_seen[index] = 1;
physical_cores++;
}
}
}
fclose(fp);
return physical_cores;
}
int main() {
int cores = get_physical_cores();
if (cores != -1) {
printf("物理核心数量: %d\n", cores);
}
return 0;
}
代码解释:
fopen
打开/proc/cpuinfo
文件。fgets
逐行读取文件内容。sscanf
从每一行中解析出physical id
和core id
。- 用
core_seen
数组去重,如果physical id
与core id
第一次出现, 则递增物理核心数量。 fclose
关闭文件。
安全建议:
这个方法依赖于 /proc/cpuinfo
文件的格式。 虽然文件格式相对稳定,但未来发生改变也说不定。 使用这个方法需要注意错误处理。
方法二:利用 sysfs 文件系统
/sys/devices/system/cpu
目录下包含有关CPU的信息,比 /proc/cpuinfo
组织得更好。我们可以通过读取这里的文件来获得物理核心数量。
原理:
- 遍历
/sys/devices/system/cpu/cpu[0-9]*
目录。 - 对于每个
cpu[0-9]*
目录,读取其下的topology/core_id
和topology/physical_package_id
文件。 - 使用一个数据结构记录每个 (physical_package_id, core_id) 对。
- 不重复的 (physical_package_id, core_id) 对的数量就是物理核心数量。
代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
int get_physical_cores_sysfs() {
DIR *cpu_dir;
struct dirent *entry;
int physical_cores = 0;
int core_seen[1024] = {0}; //假设有最多有1024 (physical_package_id, core_id) 组合
char path[256];
char core_id[32];
char physical_package_id[32];
FILE *fp;
int index = 0;
cpu_dir = opendir("/sys/devices/system/cpu");
if (cpu_dir == NULL) {
perror("无法打开 /sys/devices/system/cpu");
return -1;
}
while ((entry = readdir(cpu_dir)) != NULL) {
if (strncmp(entry->d_name, "cpu", 3) == 0 && entry->d_type == DT_DIR) {
// 读取 physical_package_id
snprintf(path, sizeof(path), "/sys/devices/system/cpu/%s/topology/physical_package_id", entry->d_name);
fp = fopen(path, "r");
if (fp) {
if (fgets(physical_package_id, sizeof(physical_package_id), fp) != NULL) {
physical_package_id[strcspn(physical_package_id, "\n")] = 0; // 去掉换行符
}
fclose(fp);
}
// 读取 core_id
snprintf(path, sizeof(path), "/sys/devices/system/cpu/%s/topology/core_id", entry->d_name);
fp = fopen(path, "r");
if (fp)
{
if (fgets(core_id, sizeof(core_id), fp) != NULL) {
core_id[strcspn(core_id, "\n")] = 0;// 去掉换行符
}
fclose(fp);
}
index = atoi(physical_package_id) * 512 + atoi(core_id); //计算索引值
if(core_seen[index] == 0)
{
core_seen[index] = 1;
physical_cores++;
}
}
}
closedir(cpu_dir);
return physical_cores;
}
int main() {
int cores = get_physical_cores_sysfs();
if (cores != -1) {
printf("物理核心数量 (sysfs): %d\n", cores);
}
return 0;
}
代码解释:
opendir
打开/sys/devices/system/cpu
目录。readdir
遍历目录下的条目。strncmp
检查条目名称是否以 "cpu" 开头,并且类型是否是目录(DT_DIR
)。snprintf
构建文件路径。fopen
、fgets
读取core_id
和physical_package_id
文件的内容。- 用
core_seen
数组去重,如果physical_package_id
与core_id
组合是第一次出现,则递增物理核心数量。 fclose
关闭文件。closedir
关闭目录。
安全建议:
此方法依赖于 sysfs 文件系统的结构。 与 /proc/cpuinfo
相比,sysfs
文件系统更稳定,推荐使用。
进阶技巧:libcpuid
如果需要获取更详细的CPU信息,例如制造商、型号、缓存大小等,可以考虑使用第三方库,比如 libcpuid
。 这个库通过 CPUID
指令来获取CPU信息,更底层。
安装(Ubuntu/Debian): sudo apt-get install libcpuid-dev
安装 (CentOS/RHEL): sudo yum install libcpuid-devel
示例代码 (需要链接 libcpuid):
#include <stdio.h>
#include <libcpuid.h>
int main() {
if (!cpuid_present()) {
printf("CPUID 指令不可用.\n");
return 1;
}
struct cpu_raw_data_t raw;
struct cpu_id_t data;
if (cpuid_get_raw_data(&raw) < 0) {
printf("无法获取 CPUID 原始数据: %s\n", cpuid_error());
return 1;
}
if (cpu_identify(&raw, &data) < 0) {
printf("无法识别 CPU: %s\n", cpuid_error());
return 1;
}
printf("物理核心数量 (libcpuid): %d\n", data.num_cores);
//还可以获取其他CPU信息.
//printf("CPU 制造商: %s\n", data.vendor_str);
//printf("CPU 型号: %s\n", data.brand_str);
return 0;
}
编译:
gcc -o cpuid_example cpuid_example.c -lcpuid
解释:
- 引入头文件
<libcpuid.h>
. cpuid_present()
检测是否支持CPUID
指令cpuid_get_raw_data()
获取原始数据cpu_identify
分析CPUID
的数据data.num_cores
成员就表示物理核心数量。- 编译需要链接
libcpuid
库。
使用libcpuid
,代码简洁, 且可以直接获取物理核心数。
三种方法都可以帮助我们在 Linux 下用 C 语言获取 CPU 的物理核心数。 可以根据实际情况选择,建议优先用方法二(读取sysfs
), 更稳定一些; 方法三 (使用libcpuid
)获取信息更丰富。