getcpu – 获取当前 CPU 和 NUMA 节点信息]()
1. 函数介绍
NUMA 是一种多处理器计算机内存设计,其中内存访问时间取决于内存相对于处理器的位置。了解当前线程在哪个 CPU 核心和 NUMA 节点上运行,有助于优化程序性能。
2. 函数原型
#define _GNU_SOURCE
#include <linux/getcpu.h>
#include <sys/syscall.h>
#include <unistd.h>
long getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache);
注意:该函数不是标准 C 库的一部分,需要通过
syscall()
调用或者包含 glibc 2.29+ 版本后才能直接调用。
3. 功能
获取当前线程正在运行的 CPU 核心编号和 NUMA 节点编号。这是一个轻量级的操作,通常用于性能监控和调度优化。
4. 参数
unsigned *cpu
: 指向存储 CPU 核心编号的变量的指针- 如果为
NULL
:不返回 CPU 信息 - 如果非
NULL
:存储当前 CPU 核心编号(从 0 开始)
- 如果为
unsigned *node
: 指向存储 NUMA 节点编号的变量的指针- 如果为
NULL
:不返回 NUMA 节点信息 - 如果非
NULL
:存储当前 NUMA 节点编号(从 0 开始)
- 如果为
struct getcpu_cache *tcache
: 缓存结构体指针,用于优化性能- 通常传入
NULL
(内核会使用默认缓存机制) - 主要供内核内部使用
- 通常传入
5. 返回值
- 成功时返回 0
- 失败时返回 -1,并设置
errno
6. 常见 errno 错误码
EFAULT
: 指针参数指向无效内存地址EINVAL
: 无效参数ENOSYS
: 系统不支持该功能
7. 相似函数,或关联函数
sched_getcpu()
: glibc 提供的获取当前 CPU 的便捷函数sched_setaffinity()
: 设置进程 CPU 亲和性sched_getaffinity()
: 获取进程 CPU 亲和性pthread_setaffinity_np()
: 设置线程 CPU 亲和性pthread_getaffinity_np()
: 获取线程 CPU 亲和性/proc/cpuinfo
: 查看 CPU 信息/sys/devices/system/cpu/
: CPU 拓扑信息numactl
: NUMA 控制工具
8. 示例代码
示例1:基本使用 – 获取当前 CPU 和 NUMA 节点信息
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/getcpu.h>
#include <errno.h>
#include <string.h>
#ifndef SYS_getcpu
# define SYS_getcpu 309 // x86_64 架构下的系统调用号
#endif
int main() {
unsigned cpu, node;
long result;
printf("=== 获取当前 CPU 和 NUMA 节点信息 ===\n");
// 使用 syscall 调用 getcpu
result = syscall(SYS_getcpu, &cpu, &node, NULL);
if (result == -1) {
printf("getcpu 调用失败: %s\n", strerror(errno));
return 1;
}
printf("当前线程运行信息:\n");
printf(" CPU 核心编号: %u\n", cpu);
printf(" NUMA 节点编号: %u\n", node);
// 验证系统 CPU 数量
long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
printf(" 系统在线 CPU 数: %ld\n", nprocs);
if (cpu >= nprocs) {
printf(" 警告: CPU 编号超出系统 CPU 数范围\n");
}
return 0;
}
示例2:使用 glibc 提供的 sched_getcpu 函数
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
int main() {
int cpu;
printf("=== 使用 sched_getcpu 获取 CPU 信息 ===\n");
cpu = sched_getcpu();
if (cpu == -1) {
perror("sched_getcpu 调用失败");
return 1;
}
printf("当前 CPU 核心编号: %d\n", cpu);
// 获取系统信息进行验证
long nprocs_conf = sysconf(_SC_NPROCESSORS_CONF);
long nprocs_onln = sysconf(_SC_NPROCESSORS_ONLN);
printf("系统配置 CPU 数: %ld\n", nprocs_conf);
printf("系统在线 CPU 数: %ld\n", nprocs_onln);
if (cpu >= nprocs_onln) {
printf("警告: CPU 编号超出在线 CPU 范围\n");
}
return 0;
}
示例3:监控 CPU 迁移情况
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/getcpu.h>
#include <sched.h>
#include <time.h>
#ifndef SYS_getcpu
# define SYS_getcpu 309
#endif
void monitor_cpu_migration(int duration_seconds) {
unsigned cpu, node;
unsigned last_cpu = (unsigned)-1;
unsigned last_node = (unsigned)-1;
int migrations = 0;
time_t start_time = time(NULL);
time_t current_time;
printf("开始监控 CPU 迁移情况 (%d 秒)...\n", duration_seconds);
printf("%-10s %-6s %-6s %-15s\n", "时间", "CPU", "Node", "状态");
printf("%-10s %-6s %-6s %-15s\n", "----", "---", "----", "----");
while ((current_time = time(NULL)) - start_time < duration_seconds) {
if (syscall(SYS_getcpu, &cpu, &node, NULL) == 0) {
char status[20] = "运行中";
if (last_cpu != (unsigned)-1) {
if (cpu != last_cpu) {
snprintf(status, sizeof(status), "CPU迁移 %u->%u", last_cpu, cpu);
migrations++;
} else if (node != last_node) {
snprintf(status, sizeof(status), "Node迁移 %u->%u", last_node, node);
migrations++;
}
}
printf("%-10ld %-6u %-6u %-15s\n",
current_time - start_time, cpu, node, status);
last_cpu = cpu;
last_node = node;
}
usleep(500000); // 休眠 0.5 秒
}
printf("\n监控结束:\n");
printf(" 总迁移次数: %d\n", migrations);
printf(" 平均迁移间隔: %.2f 秒\n",
migrations > 0 ? (double)duration_seconds / migrations : 0.0);
}
int main() {
printf("=== CPU 迁移监控演示 ===\n");
// 获取初始信息
unsigned cpu, node;
if (syscall(SYS_getcpu, &cpu, &node, NULL) == 0) {
printf("初始位置: CPU %u, Node %u\n", cpu, node);
}
// 监控 10 秒钟的 CPU 迁移情况
monitor_cpu_migration(10);
return 0;
}
示例4:多线程环境下的 CPU 信息
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <linux/getcpu.h>
#include <sched.h>
#ifndef SYS_getcpu
# define SYS_getcpu 309
#endif
void* thread_function(void *arg) {
int thread_id = *(int*)arg;
unsigned cpu, node;
cpu_set_t cpuset;
int ret;
// 获取当前线程的 CPU 信息
if (syscall(SYS_getcpu, &cpu, &node, NULL) == 0) {
printf("线程 %d: 初始位置 CPU %u, Node %u\n", thread_id, cpu, node);
}
// 获取当前 CPU 亲和性
ret = pthread_getaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
if (ret == 0) {
printf("线程 %d: 可运行在 CPU ", thread_id);
for (int i = 0; i < CPU_SETSIZE; i++) {
if (CPU_ISSET(i, &cpuset)) {
printf("%d ", i);
}
}
printf("\n");
}
// 执行一些工作
for (int i = 0; i < 5; i++) {
sleep(1);
if (syscall(SYS_getcpu, &cpu, &node, NULL) == 0) {
printf("线程 %d: 当前位置 CPU %u, Node %u\n", thread_id, cpu, node);
}
}
return NULL;
}
int main() {
pthread_t threads[3];
int thread_ids[3] = {1, 2, 3};
printf("=== 多线程 CPU 信息演示 ===\n");
// 创建多个线程
for (int i = 0; i < 3; i++) {
if (pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]) != 0) {
perror("创建线程失败");
return 1;
}
}
// 等待所有线程完成
for (int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
printf("所有线程执行完成\n");
return 0;
}
9. NUMA 系统信息查看
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void print_numa_info() {
FILE *fp;
char line[256];
printf("\n=== NUMA 系统信息 ===\n");
// 检查 NUMA 支持
if (access("/proc/numa_maps", F_OK) == 0) {
printf("系统支持 NUMA\n");
} else {
printf("系统不支持 NUMA 或无访问权限\n");
return;
}
// 查看 NUMA 节点信息
fp = fopen("/sys/devices/system/node/online", "r");
if (fp) {
if (fgets(line, sizeof(line), fp)) {
printf("在线 NUMA 节点: %s", line);
}
fclose(fp);
}
// 查看 CPU 拓扑
system("lscpu | grep -E 'NUMA|CPU(s)'");
}
10. 性能优化应用场景
getcpu
在以下场景中非常有用:
场景1:线程绑定优化
#include <sched.h>
#include <pthread.h>
void bind_thread_to_cpu(int target_cpu) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(target_cpu, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
}
场景2:内存分配优化
#include <numa.h>
void* allocate_local_memory(size_t size) {
if (numa_available() == -1) {
return malloc(size); // NUMA 不可用,使用普通分配
}
return numa_alloc_local(size); // 分配在本地 NUMA 节点
}
11. 实际系统中的应用
总结
getcpu
是一个重要的系统调用,用于获取当前线程的 CPU 和 NUMA 节点信息。关键要点:
在编写高性能应用程序时,了解和利用 CPU 和 NUMA 信息可以显著提升程序性能,特别是在多核和 NUMA 系统上。】