74. getcpu - 获取当前 CPU 和 NUMA 节点信息 見出しへのリンク
1. 函数介绍 見出しへのリンク
getcpu
是一个 Linux 系统调用,用于获取当前线程正在运行的 CPU 核心编号和 NUMA(Non-Uniform Memory Access)节点编号。这个函数对于性能分析、负载均衡、线程亲和性设置等场景非常有用。
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. 实际系统中的应用 見出しへのリンク
- 数据库系统: 将查询线程绑定到特定 CPU 以提高缓存命中率
- Web 服务器: 均衡分配请求处理线程到不同 CPU 核心
- 高性能计算: 优化 MPI 进程的 CPU 和 NUMA 布局
- 实时系统: 确保关键任务在指定 CPU 上运行
- 容器技术: Docker 和 Kubernetes 中的 CPU 资源管理
总结 見出しへのリンク
getcpu
是一个重要的系统调用,用于获取当前线程的 CPU 和 NUMA 节点信息。关键要点:
- 轻量级操作: 快速获取当前执行环境信息
- NUMA 感知: 支持现代多核 NUMA 系统
- 性能优化: 为调度和内存访问优化提供基础信息
- 多线程支持: 在并发环境中准确反映线程位置
- 系统监控: 用于负载均衡和性能分析工具
在编写高性能应用程序时,了解和利用 CPU 和 NUMA 信息可以显著提升程序性能,特别是在多核和 NUMA 系统上。