get_mempolicy 函数详解
get_mempolicy
是 Linux 系统中用于查询 NUMA 内存分配策略的系统调用,可获取进程或指定内存区域的 NUMA 节点分配策略。该函数支持查询默认策略、特定地址策略及允许使用的 NUMA 节点集合,适用于优化多核 NUMA 架构下的内存访问性能。函数通过 mode 参数返回策略类型(如默认、优先节点、绑定节点等),nodemask 返回节点掩码,flags 控制查询行为。典型应用场景包括高性能计算和大内存应用,需配合 set_mempolicy 和 mbind 等函数使用。
1. 函数介绍
get_mempolicy
是 Linux 系统中用于获取内存策略(Memory Policy)的系统调用。可以把内存策略想象成”内存分配的导航系统”——它告诉操作系统应该将进程的内存分配到哪个 NUMA 节点上。
在现代多核系统中,特别是 NUMA(Non-Uniform Memory Access)架构中,不同的 CPU 核心访问不同内存节点的速度是不一样的。就像你去超市买东西,有些商品在你附近的货架上(访问快),有些在远处的货架上(访问慢)。内存策略就是告诉系统:“优先在我附近的内存区域分配内存”,从而提高程序性能。
get_mempolicy
允许你查询当前的内存分配策略,了解系统是如何为你的进程分配内存的。
2. 函数原型
#include <numaif.h>
long get_mempolicy(int *mode, unsigned long *nodemask,
unsigned long maxnode, void *addr, unsigned long flags);
3. 功能
get_mempolicy
函数用于获取当前进程或指定内存地址的内存分配策略。它可以查询:
- 进程默认的内存策略
- 特定内存地址的内存策略
- 系统支持的 NUMA 节点信息
4. 参数
- mode: 指向整型变量的指针,用于存储返回的策略模式
- nodemask: 指向节点掩码数组的指针,用于存储策略涉及的 NUMA 节点
- maxnode: nodemask 数组的最大节点数
- addr: 指定内存地址(可选,用于查询特定地址的策略)
- flags: 控制操作行为的标志位
5. 策略模式(mode 参数的可能值)
模式 | 值 | 说明 |
---|---|---|
MPOL_DEFAULT | 0 | 默认策略,遵循系统默认行为 |
MPOL_PREFERRED | 1 | 优先分配到指定节点 |
MPOL_BIND | 2 | 严格绑定到指定节点集合 |
MPOL_INTERLEAVE | 3 | 在指定节点间交错分配 |
MPOL_LOCAL | 4 | 分配到本地节点(进程运行的节点) |
6. 标志位(flags 参数)
标志 | 说明 |
---|---|
0 | 查询进程默认策略 |
MPOL_F_NODE | 返回节点信息 |
MPOL_F_ADDR | 查询指定地址 addr 的策略 |
MPOL_F_MEMS_ALLOWED | 返回进程允许使用的节点集合 |
7. 返回值
- 成功: 返回 0
- 失败: 返回 -1,并设置相应的 errno 错误码
常见错误码:
EFAULT
: 地址参数无效EINVAL
: 参数无效ENOMEM
: 内存不足
8. 相似函数或关联函数
- set_mempolicy: 设置内存策略
- mbind: 为特定内存区域设置内存策略
- getcpu: 获取当前 CPU 和 NUMA 节点信息
- numa_ functions*: libnuma 库提供的 NUMA 操作函数
- mbind: 绑定内存到特定节点
9. 示例代码
示例1:基础用法 – 获取进程默认内存策略
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <numaif.h>
#include <string.h>
// 将节点掩码转换为可读的字符串
void print_nodemask(unsigned long *nodemask, unsigned long maxnode) {
printf("节点掩码: ");
int printed = 0;
for (unsigned long i = 0; i < maxnode; i++) {
if (nodemask[i / (sizeof(unsigned long) * 8)] &
(1UL << (i % (sizeof(unsigned long) * 8)))) {
if (printed > 0) printf(",");
printf("%lu", i);
printed++;
}
}
if (printed == 0) {
printf("(无节点)");
}
printf("\n");
}
// 获取策略模式的字符串描述
const char* get_policy_name(int mode) {
switch (mode) {
case MPOL_DEFAULT:
return "MPOL_DEFAULT (默认策略)";
case MPOL_PREFERRED:
return "MPOL_PREFERRED (优先节点)";
case MPOL_BIND:
return "MPOL_BIND (绑定节点)";
case MPOL_INTERLEAVE:
return "MPOL_INTERLEAVE (交错分配)";
case MPOL_LOCAL:
return "MPOL_LOCAL (本地节点)";
default:
return "未知策略";
}
}
int main() {
int mode;
unsigned long nodemask[16]; // 支持最多 16 * sizeof(unsigned long) * 8 个节点
unsigned long maxnode = sizeof(nodemask) * 8;
printf("=== 获取进程默认内存策略 ===\n\n");
// 获取当前进程的默认内存策略
if (get_mempolicy(&mode, nodemask, maxnode, NULL, 0) == -1) {
perror("get_mempolicy");
printf("可能的原因:\n");
printf("1. 系统不支持 NUMA\n");
printf("2. 未链接 libnuma 库\n");
printf("3. 内核不支持此功能\n");
return 1;
}
printf("当前进程内存策略:\n");
printf("策略模式: %s (%d)\n", get_policy_name(mode), mode);
print_nodemask(nodemask, maxnode);
// 获取允许使用的节点集合
printf("\n=== 允许使用的 NUMA 节点 ===\n");
memset(nodemask, 0, sizeof(nodemask));
if (get_mempolicy(&mode, nodemask, maxnode, NULL, MPOL_F_MEMS_ALLOWED) == 0) {
printf("允许的节点: ");
print_nodemask(nodemask, maxnode);
} else {
perror("get_mempolicy MPOL_F_MEMS_ALLOWED");
}
return 0;
}
示例2:查询特定内存地址的策略
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <numaif.h>
#include <string.h>
int main() {
int mode;
unsigned long nodemask[16];
unsigned long maxnode = sizeof(nodemask) * 8;
char *memory_block;
size_t block_size = 1024 * 1024; // 1MB
printf("=== 查询特定内存地址的策略 ===\n\n");
// 分配内存块
memory_block = mmap(NULL, block_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory_block == MAP_FAILED) {
perror("mmap");
return 1;
}
printf("分配内存块: %p - %p (大小: %zu 字节)\n",
memory_block, memory_block + block_size - 1, block_size);
// 查询整个内存块的策略
printf("\n--- 查询内存块起始地址的策略 ---\n");
if (get_mempolicy(&mode, nodemask, maxnode, memory_block, MPOL_F_ADDR) == 0) {
printf("地址 %p 的策略: %d\n", memory_block, mode);
} else {
perror("get_mempolicy for memory block");
}
// 查询内存块中间地址的策略
printf("\n--- 查询内存块中间地址的策略 ---\n");
char *middle_addr = memory_block + block_size / 2;
if (get_mempolicy(&mode, nodemask, maxnode, middle_addr, MPOL_F_ADDR) == 0) {
printf("地址 %p 的策略: %d\n", middle_addr, mode);
} else {
perror("get_mempolicy for middle address");
}
// 使用内存
memset(memory_block, 0xAA, block_size);
printf("\n已使用内存块\n");
// 再次查询策略
printf("\n--- 使用内存后查询策略 ---\n");
if (get_mempolicy(&mode, nodemask, maxnode, memory_block, MPOL_F_ADDR) == 0) {
printf("地址 %p 的策略: %d\n", memory_block, mode);
} else {
perror("get_mempolicy after usage");
}
// 释放内存
if (munmap(memory_block, block_size) == -1) {
perror("munmap");
return 1;
}
printf("\n内存块已释放\n");
return 0;
}
示例3:完整的 NUMA 信息查询工具
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <numaif.h>
#include <sched.h>
#include <string.h>
// 简单的 CPU 和 NUMA 信息查询
void print_cpu_and_numa_info() {
#ifdef SYS_getcpu
unsigned cpu, node;
if (syscall(SYS_getcpu, &cpu, &node, NULL) == 0) {
printf("当前运行在 CPU %u, NUMA 节点 %u\n", cpu, node);
} else {
printf("无法获取 CPU/NUMA 信息\n");
}
#else
printf("系统不支持 getcpu 系统调用\n");
#endif
}
// 显示系统 NUMA 拓扑信息
void print_numa_topology() {
long max_nodes = sysconf(_SC_NPROCESSORS_ONLN);
printf("系统在线 CPU 数: %ld\n", max_nodes);
// 尝试获取 NUMA 节点数
FILE *fp = fopen("/sys/devices/system/node/online", "r");
if (fp) {
char buffer[256];
if (fgets(buffer, sizeof(buffer), fp)) {
printf("在线 NUMA 节点: %s", buffer);
}
fclose(fp);
} else {
printf("无法读取 NUMA 节点信息\n");
}
}
// 详细分析内存策略
void analyze_memory_policy() {
int mode;
unsigned long nodemask[16];
unsigned long maxnode = sizeof(nodemask) * 8;
printf("\n=== 详细内存策略分析 ===\n");
// 1. 获取默认策略
if (get_mempolicy(&mode, nodemask, maxnode, NULL, 0) == 0) {
printf("1. 进程默认策略: %d\n", mode);
}
// 2. 获取允许的节点
memset(nodemask, 0, sizeof(nodemask));
if (get_mempolicy(&mode, nodemask, maxnode, NULL, MPOL_F_MEMS_ALLOWED) == 0) {
printf("2. 允许使用的节点: ");
int count = 0;
for (unsigned long i = 0; i < maxnode; i++) {
if (nodemask[i / (sizeof(unsigned long) * 8)] &
(1UL << (i % (sizeof(unsigned long) * 8)))) {
if (count > 0) printf(", ");
printf("%lu", i);
count++;
}
}
printf("\n");
}
// 3. 获取本地策略
memset(nodemask, 0, sizeof(nodemask));
if (get_mempolicy(&mode, nodemask, maxnode, NULL, MPOL_F_NODE) == 0) {
printf("3. 本地节点策略: %d\n", mode);
}
}
// 测试不同内存分配的策略
void test_memory_allocation_policies() {
printf("\n=== 内存分配策略测试 ===\n");
// 分配小块内存
char *small_alloc = malloc(1024);
if (small_alloc) {
printf("小块内存分配 (%p): ", small_alloc);
int mode;
unsigned long nodemask[16];
if (get_mempolicy(&mode, nodemask, sizeof(nodemask) * 8,
small_alloc, MPOL_F_ADDR) == 0) {
printf("策略 %d\n", mode);
} else {
printf("无法查询策略\n");
}
free(small_alloc);
}
// 分配大块内存
char *large_alloc = malloc(10 * 1024 * 1024); // 10MB
if (large_alloc) {
printf("大块内存分配 (%p): ", large_alloc);
int mode;
unsigned long nodemask[16];
if (get_mempolicy(&mode, nodemask, sizeof(nodemask) * 8,
large_alloc, MPOL_F_ADDR) == 0) {
printf("策略 %d\n", mode);
} else {
printf("无法查询策略\n");
}
free(large_alloc);
}
}
int main() {
printf("=== NUMA 内存策略查询工具 ===\n\n");
// 显示基本信息
print_cpu_and_numa_info();
print_numa_topology();
// 分析内存策略
analyze_memory_policy();
// 测试内存分配
test_memory_allocation_policies();
printf("\n=== 工具使用说明 ===\n");
printf("此工具显示当前进程的 NUMA 内存策略信息。\n");
printf("如果显示错误,可能原因:\n");
printf("1. 系统不是 NUMA 架构\n");
printf("2. 未安装或链接 libnuma 库\n");
printf("3. 内核版本不支持 NUMA 功能\n");
return 0;
}
编译和运行说明
# 编译示例程序(需要链接 libnuma)
gcc -o get_mempolicy_example1 example1.c -lnuma
gcc -o get_mempolicy_example2 example2.c -lnuma
gcc -o get_mempolicy_example3 example3.c -lnuma
# 如果系统没有 libnuma,可以尝试:
gcc -o get_mempolicy_example1 example1.c
gcc -o get_mempolicy_example2 example2.c
# 运行示例
./get_mempolicy_example1
./get_mempolicy_example2
./get_mempolicy_example3
安装 libnuma(如果需要)
# Ubuntu/Debian
sudo apt-get install libnuma-dev
# CentOS/RHEL
sudo yum install numactl-devel
# Fedora
sudo dnf install numactl-devel
重要注意事项
- 系统要求: 需要 NUMA 支持的硬件和内核
- 库依赖: 通常需要链接 libnuma 库
- 权限: 一般不需要特殊权限
- 移植性: NUMA 功能是 Linux 特有的
- 性能影响: 查询内存策略本身开销很小
策略模式详解
- MPOL_DEFAULT: 使用系统默认的内存分配策略
- MPOL_PREFERRED: 优先在指定节点分配内存,如果该节点不可用则在其他节点分配
- MPOL_BIND: 严格限制只能在指定节点集合中分配内存
- MPOL_INTERLEAVE: 在多个节点间交错分配内存(适合大内存应用)
- MPOL_LOCAL: 总是在进程运行的本地节点分配内存
实际应用场景
- 高性能计算: 优化大规模数值计算的内存访问模式
- 数据库系统: 将数据分配到合适的 NUMA 节点以提高访问速度
- Web 服务器: 优化多线程应用的内存局部性
- 科学计算: 大型矩阵运算的内存布局优化
- 虚拟化环境: 为虚拟机合理分配物理内存节点
这些示例展示了 get_mempolicy
函数的各种使用方法,从基本的策略查询到完整的 NUMA 信息分析工具,帮助你理解和掌握 Linux NUMA 内存管理机制。
get_mempolicy系统调用, Linux get_mempolicy函数, get_mempolicy使用示例, 查询内存策略Linux, 理解get_mempolicy工作原理, Linux内存管理API, 系统调用与内存分配, 高级Linux编程技巧, get_mempolicy参数解析, 优化Linux性能的系统调用