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 系统上。】