103. move_pages - 移动页面到指定 NUMA 节点 链接到标题
1. 函数介绍 链接到标题
move_pages
是一个 Linux 系统调用,用于将指定的内存页面移动到特定的 NUMA(Non-Uniform Memory Access)节点。与 migrate_pages
不同,move_pages
允许精确控制哪些页面移动到哪个节点,提供了更细粒度的 NUMA 内存管理能力。
2. 函数原型 链接到标题
#define _GNU_SOURCE
#include <numa.h>
#include <numaif.h>
long move_pages(int pid, unsigned long count,
void **pages, const int *nodes,
int *status, int flags);
注意:这不是标准 C 库函数,需要使用 libnuma 库。
3. 功能 链接到标题
将指定进程的特定内存页面移动到指定的 NUMA 节点。这个函数允许应用程序精确控制内存页面的 NUMA 位置,以优化内存访问性能。
4. 参数 链接到标题
int pid
: 目标进程的进程 ID0
: 表示当前进程- 正整数: 指定具体进程的 ID
unsigned long count
: 要移动的页面数量void **pages
: 指向页面地址数组的指针const int *nodes
: 指向目标节点数组的指针int *status
: 指向状态数组的指针(用于返回操作结果)int flags
: 控制标志MPOL_MF_MOVE
: 移动所有指定的页面MPOL_MF_MOVE_ALL
: 移动所有页面(包括已修改的)
5. 返回值 链接到标题
- 成功时:返回 0
- 失败时:返回 -1,并设置
errno
6. 常见 errno 错误码 链接到标题
EPERM
: 权限不足(移动其他进程需要适当权限)ESRCH
: 指定的进程不存在EINVAL
: 参数无效(如页面地址无效)ENOMEM
: 内存不足ENODEV
: 指定的 NUMA 节点不存在EACCES
: 访问被拒绝ENOSYS
: 系统不支持 NUMA 或此功能
7. 相似函数,或关联函数 链接到标题
migrate_pages()
: 迁移进程所有页面到指定节点set_mempolicy()
: 设置内存策略get_mempolicy()
: 获取内存策略mbind()
: 将内存区域绑定到特定节点numa_*
函数族: NUMA 相关操作函数/sys/devices/system/node/
: NUMA 节点信息/proc/[pid]/numa_maps
: 进程 NUMA 内存映射
8. 示例代码 链接到标题
示例1:基本使用 - NUMA 页面移动 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#ifdef HAVE_NUMA
#include <numa.h>
#include <numaif.h>
void print_page_nodes(void *addr, size_t size) {
size_t page_size = getpagesize();
size_t num_pages = (size + page_size - 1) / page_size;
printf("内存页面 NUMA 节点分布:\n");
for (size_t i = 0; i < num_pages; i++) {
void *page_addr = (char*)addr + i * page_size;
int node;
if (get_mempolicy(&node, NULL, 0, page_addr, MPOL_F_ADDR) == 0) {
printf(" 页面 %zu (地址 %p): 节点 %d\n", i, page_addr, node);
} else {
printf(" 页面 %zu (地址 %p): 无法获取节点信息\n", i, page_addr);
}
}
}
void demonstrate_basic_move_pages() {
printf("=== move_pages 基本使用演示 ===\n");
if (numa_available() == -1) {
printf("系统不支持 NUMA\n");
return;
}
int max_node = numa_max_node();
if (max_node < 1) {
printf("系统只有一个 NUMA 节点,无法演示页面移动\n");
return;
}
printf("系统 NUMA 节点数: %d\n", max_node + 1);
// 分配内存页面
size_t page_size = getpagesize();
size_t alloc_size = 4 * page_size; // 4 个页面
void *memory = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory == MAP_FAILED) {
perror("mmap 失败");
return;
}
printf("✓ 分配了 %zu 字节内存 (%p)\n", alloc_size, memory);
// 初始化内存
memset(memory, 0x42, alloc_size);
printf("✓ 初始化内存内容\n");
// 显示初始页面分布
printf("\n初始页面分布:\n");
print_page_nodes(memory, alloc_size);
// 准备移动页面
printf("\n准备移动页面...\n");
// 获取当前页面所在的节点
int current_nodes[4];
void *pages[4];
for (int i = 0; i < 4; i++) {
pages[i] = (char*)memory + i * page_size;
if (get_mempolicy(¤t_nodes[i], NULL, 0, pages[i], MPOL_F_ADDR) != 0) {
current_nodes[i] = 0; // 默认节点
}
}
// 确定目标节点(移动到不同的节点)
int target_nodes[4];
for (int i = 0; i < 4; i++) {
target_nodes[i] = (current_nodes[i] + 1) % (max_node + 1);
printf("页面 %d: 从节点 %d 移动到节点 %d\n",
i, current_nodes[i], target_nodes[i]);
}
// 执行页面移动
int status[4];
long result = move_pages(0, 4, pages, target_nodes, status, MPOL_MF_MOVE);
if (result == -1) {
printf("页面移动失败: %s\n", strerror(errno));
switch (errno) {
case EPERM:
printf(" 原因: 权限不足\n");
break;
case ENOSYS:
printf(" 原因: 系统不支持 move_pages\n");
break;
case EINVAL:
printf(" 原因: 参数无效\n");
break;
default:
printf(" 原因: 其他错误\n");
break;
}
} else {
printf("✓ 页面移动操作完成\n");
printf("状态信息:\n");
for (int i = 0; i < 4; i++) {
if (status[i] >= 0) {
printf(" 页面 %d: 移动到节点 %d\n", i, status[i]);
} else {
printf(" 页面 %d: 移动失败 (%s)\n", i, strerror(-status[i]));
}
}
}
// 显示移动后的页面分布
printf("\n移动后页面分布:\n");
print_page_nodes(memory, alloc_size);
// 清理资源
munmap(memory, alloc_size);
printf("✓ 内存释放完成\n");
}
#else
void demonstrate_basic_move_pages() {
printf("=== move_pages 基本使用演示 ===\n");
printf("编译时未启用 NUMA 支持\n");
printf("请安装 libnuma-dev 并使用 -DHAVE_NUMA 重新编译\n");
}
#endif
int main() {
printf("=== move_pages 功能演示 ===\n");
#ifdef HAVE_NUMA
if (numa_available() == -1) {
printf("系统支持 NUMA 但当前不可用\n");
return 1;
}
printf("✓ NUMA 支持可用\n");
demonstrate_basic_move_pages();
#else
printf("ℹ 编译时未启用 NUMA 支持\n");
demonstrate_basic_move_pages();
#endif
return 0;
}
示例2:错误处理和特殊情况 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#ifdef HAVE_NUMA
#include <numa.h>
#include <numaif.h>
void test_move_pages_errors() {
printf("=== move_pages 错误处理测试 ===\n");
if (numa_available() == -1) {
printf("系统不支持 NUMA\n");
return;
}
// 分配测试内存
size_t page_size = getpagesize();
void *memory = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory == MAP_FAILED) {
perror("mmap 失败");
return;
}
printf("分配测试内存: %p\n", memory);
// 测试1: 无效的页面地址
printf("\n测试1: 无效的页面地址\n");
void *invalid_pages[] = {(void*)0x1, (void*)0xFFFFFFFF};
int target_nodes[] = {0, 0};
int status[2];
long result = move_pages(0, 2, invalid_pages, target_nodes, status, MPOL_MF_MOVE);
if (result == -1) {
printf(" 结果: 失败 - %s\n", strerror(errno));
if (errno == EINVAL) {
printf(" 原因: 无效的页面地址\n");
}
}
// 测试2: 无效的目标节点
printf("\n测试2: 无效的目标节点\n");
void *valid_pages[] = {memory, (char*)memory + page_size};
int invalid_nodes[] = {-1, 999}; // 假设这些节点不存在
result = move_pages(0, 2, valid_pages, invalid_nodes, status, MPOL_MF_MOVE);
if (result == -1) {
printf(" 结果: 失败 - %s\n", strerror(errno));
if (errno == ENODEV) {
printf(" 原因: 无效的目标节点\n");
}
}
// 测试3: 空参数
printf("\n测试3: 空参数\n");
result = move_pages(0, 0, NULL, NULL, NULL, MPOL_MF_MOVE);
if (result == -1) {
printf(" 结果: 失败 - %s\n", strerror(errno));
} else {
printf(" 结果: 成功 (空操作)\n");
}
// 测试4: 无效的进程ID
printf("\n测试4: 无效的进程ID\n");
result = move_pages(-1, 1, valid_pages, target_nodes, status, MPOL_MF_MOVE);
if (result == -1) {
printf(" 结果: 失败 - %s\n", strerror(errno));
if (errno == ESRCH) {
printf(" 原因: 进程不存在\n");
}
}
// 清理资源
munmap(memory, 2 * page_size);
}
void demonstrate_move_flags() {
printf("\n=== move_pages 标志演示 ===\n");
if (numa_available() == -1) {
printf("系统不支持 NUMA\n");
return;
}
// 分配内存
size_t page_size = getpagesize();
void *memory = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory == MAP_FAILED) {
perror("mmap 失败");
return;
}
printf("分配内存用于标志测试: %p\n", memory);
void *pages[] = {memory};
int target_nodes[] = {0};
int status[1];
// 测试 MPOL_MF_MOVE 标志
printf("测试 MPOL_MF_MOVE 标志:\n");
long result = move_pages(0, 1, pages, target_nodes, status, MPOL_MF_MOVE);
if (result == 0) {
printf(" ✓ MPOL_MF_MOVE 执行成功\n");
} else {
printf(" ✗ MPOL_MF_MOVE 执行失败: %s\n", strerror(errno));
}
// 清理
munmap(memory, page_size);
}
#else
void test_move_pages_errors() {
printf("=== move_pages 错误处理测试 ===\n");
printf("编译时未启用 NUMA 支持\n");
}
void demonstrate_move_flags() {
printf("=== move_pages 标志演示 ===\n");
printf("编译时未启用 NUMA 支持\n");
}
#endif
int main() {
printf("=== move_pages 错误处理和标志演示 ===\n");
#ifdef HAVE_NUMA
if (numa_available() == -1) {
printf("系统不支持 NUMA\n");
return 1;
}
test_move_pages_errors();
demonstrate_move_flags();
#else
test_move_pages_errors();
demonstrate_move_flags();
#endif
return 0;
}
示例3:高级 NUMA 页面管理 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#ifdef HAVE_NUMA
#include <numa.h>
#include <numaif.h>
typedef struct {
void *addr;
int current_node;
int target_node;
size_t size;
} page_info_t;
void initialize_memory_pattern(void *memory, size_t size) {
char *ptr = (char*)memory;
for (size_t i = 0; i < size; i++) {
ptr[i] = (char)(i % 256);
}
}
int verify_memory_pattern(void *memory, size_t size) {
char *ptr = (char*)memory;
for (size_t i = 0; i < size; i++) {
if (ptr[i] != (char)(i % 256)) {
return 0; // 模式不匹配
}
}
return 1; // 模式匹配
}
void demonstrate_advanced_page_movement() {
printf("=== 高级 NUMA 页面移动演示 ===\n");
if (numa_available() == -1) {
printf("系统不支持 NUMA\n");
return;
}
int max_node = numa_max_node();
if (max_node < 1) {
printf("系统只有一个 NUMA 节点\n");
return;
}
printf("系统 NUMA 节点: 0 到 %d\n", max_node);
// 分配较大内存块
size_t page_size = getpagesize();
size_t alloc_size = 16 * page_size; // 16 个页面
void *memory = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory == MAP_FAILED) {
perror("mmap 失败");
return;
}
printf("✓ 分配 %zu 字节内存 (%p)\n", alloc_size, memory);
// 初始化内存模式
initialize_memory_pattern(memory, alloc_size);
printf("✓ 初始化内存模式\n");
// 验证初始内存
if (verify_memory_pattern(memory, alloc_size)) {
printf("✓ 内存模式验证通过\n");
} else {
printf("✗ 内存模式验证失败\n");
munmap(memory, alloc_size);
return;
}
// 获取每个页面的当前节点
int page_count = 16;
void *pages[16];
int current_nodes[16];
int target_nodes[16];
int status[16];
printf("\n页面初始分布:\n");
for (int i = 0; i < page_count; i++) {
pages[i] = (char*)memory + i * page_size;
if (get_mempolicy(¤t_nodes[i], NULL, 0, pages[i], MPOL_F_ADDR) == 0) {
// 计算目标节点(轮转到下一个节点)
target_nodes[i] = (current_nodes[i] + 1) % (max_node + 1);
printf(" 页面 %2d: 节点 %d -> 节点 %d\n",
i, current_nodes[i], target_nodes[i]);
} else {
current_nodes[i] = 0;
target_nodes[i] = 1;
}
}
// 执行页面移动
printf("\n执行页面移动...\n");
clock_t start_time = clock();
long result = move_pages(0, page_count, pages, target_nodes, status, MPOL_MF_MOVE);
clock_t end_time = clock();
double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC * 1000;
if (result == -1) {
printf("✗ 页面移动失败: %s\n", strerror(errno));
munmap(memory, alloc_size);
return;
}
printf("✓ 页面移动完成,耗时: %.2f 毫秒\n", elapsed_time);
// 分析移动结果
int success_count = 0;
int failed_count = 0;
for (int i = 0; i < page_count; i++) {
if (status[i] >= 0) {
success_count++;
} else {
failed_count++;
printf(" 页面 %d 移动失败: %s\n", i, strerror(-status[i]));
}
}
printf("移动结果统计:\n");
printf(" 成功: %d 个页面\n", success_count);
printf(" 失败: %d 个页面\n", failed_count);
// 验证移动后的页面分布
printf("\n移动后页面分布:\n");
for (int i = 0; i < page_count; i++) {
int new_node;
if (get_mempolicy(&new_node, NULL, 0, pages[i], MPOL_F_ADDR) == 0) {
printf(" 页面 %2d: 节点 %d %s\n",
i, new_node,
(new_node == target_nodes[i]) ? "✓" : "✗");
}
}
// 验证内存内容是否完整
printf("\n内存完整性验证:\n");
if (verify_memory_pattern(memory, alloc_size)) {
printf("✓ 内存内容完整,移动过程未损坏数据\n");
} else {
printf("✗ 内存内容损坏\n");
}
// 清理资源
munmap(memory, alloc_size);
printf("✓ 资源清理完成\n");
}
void show_numa_page_statistics() {
printf("\n=== NUMA 页面统计信息 ===\n");
// 显示系统 NUMA 信息
int max_node = numa_max_node();
printf("系统 NUMA 节点数: %d\n", max_node + 1);
// 显示当前进程的 NUMA 内存分布
printf("当前进程 NUMA 内存分布:\n");
system("cat /proc/self/numa_maps 2>/dev/null | head -10 || echo '无法读取 numa_maps'");
// 显示 NUMA 统计信息
printf("\nNUMA 统计信息:\n");
system("numastat 2>/dev/null | head -8 || echo 'numastat 不可用'");
}
#else
void demonstrate_advanced_page_movement() {
printf("=== 高级 NUMA 页面移动演示 ===\n");
printf("编译时未启用 NUMA 支持\n");
}
void show_numa_page_statistics() {
printf("=== NUMA 页面统计信息 ===\n");
printf("编译时未启用 NUMA 支持\n");
}
#endif
int main() {
printf("=== 高级 NUMA 页面管理演示 ===\n");
#ifdef HAVE_NUMA
if (numa_available() == -1) {
printf("系统支持 NUMA 但当前不可用\n");
return 1;
}
printf("✓ NUMA 支持可用\n");
demonstrate_advanced_page_movement();
show_numa_page_statistics();
#else
printf("ℹ 编译时未启用 NUMA 支持\n");
demonstrate_advanced_page_movement();
show_numa_page_statistics();
#endif
return 0;
}
示例4:NUMA 页面管理工具 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#ifdef HAVE_NUMA
#include <numa.h>
#include <numaif.h>
void print_process_numa_info(pid_t pid) {
printf("=== 进程 %d 的 NUMA 信息 ===\n", pid);
char numa_maps_path[64];
snprintf(numa_maps_path, sizeof(numa_maps_path), "/proc/%d/numa_maps", pid);
FILE *fp = fopen(numa_maps_path, "r");
if (fp) {
char line[512];
int line_count = 0;
printf("%-16s %-8s %-10s %s\n", "地址范围", "节点", "大小", "详细信息");
printf("%-16s %-8s %-10s %s\n", "--------", "----", "----", "--------");
while (fgets(line, sizeof(line), fp) && line_count < 15) {
// 简单解析 numa_maps 行
char *token = strtok(line, " ");
if (token) {
char address[32] = "";
char node_info[64] = "";
strcpy(address, token);
token = strtok(NULL, " ");
while (token) {
if (strstr(token, "=") && strstr(token, "N")) {
strcpy(node_info, token);
break;
}
token = strtok(NULL, " ");
}
printf("%-16s %-8s %-10s %s\n", address,
node_info, "", line);
}
line_count++;
}
if (line_count >= 15) {
printf("... (显示前15行)\n");
}
fclose(fp);
} else {
printf("无法读取 %s: %s\n", numa_maps_path, strerror(errno));
}
}
void interactive_numa_page_manager() {
int choice;
pid_t target_pid = getpid();
while (1) {
printf("\n=== NUMA 页面管理工具 ===\n");
printf("当前目标进程: %d\n", target_pid);
printf("1. 显示进程 NUMA 信息\n");
printf("2. 显示系统 NUMA 信息\n");
printf("3. 设置目标进程 PID\n");
printf("4. 移动指定页面\n");
printf("5. 显示页面节点分布\n");
printf("6. NUMA 性能统计\n");
printf("0. 退出\n");
printf("请选择操作: ");
if (scanf("%d", &choice) != 1) {
printf("输入无效\n");
while (getchar() != '\n'); // 清空输入缓冲区
continue;
}
switch (choice) {
case 1:
print_process_numa_info(target_pid);
break;
case 2: {
printf("系统 NUMA 信息:\n");
int max_node = numa_max_node();
printf(" 最大节点: %d\n", max_node);
struct bitmask *nodes = numa_get_mems_allowed();
printf(" 在线节点: ");
for (int i = 0; i <= max_node; i++) {
if (numa_bitmask_isbitset(nodes, i)) {
printf("%d ", i);
}
}
printf("\n");
numa_free_nodemask(nodes);
printf(" CPU 到节点映射:\n");
for (int i = 0; i <= max_node; i++) {
struct bitmask *cpus = numa_allocate_cpumask();
if (numa_node_to_cpus(i, cpus) == 0) {
printf(" 节点 %d: ", i);
for (int j = 0; j < numa_num_possible_cpus(); j++) {
if (numa_bitmask_isbitset(cpus, j)) {
printf("CPU%d ", j);
}
}
printf("\n");
}
numa_free_cpumask(cpus);
}
break;
}
case 3:
printf("输入目标进程 PID: ");
if (scanf("%d", &target_pid) != 1) {
printf("输入无效\n");
target_pid = getpid();
}
break;
case 4: {
printf("移动页面功能需要详细的地址和节点信息\n");
printf("此功能在交互模式下较为复杂,建议使用编程方式\n");
break;
}
case 5: {
printf("显示页面节点分布需要具体的内存地址\n");
printf("请提供要分析的内存地址范围\n");
break;
}
case 6: {
printf("NUMA 性能统计:\n");
system("numastat 2>/dev/null | head -10 || echo 'numastat 不可用'");
break;
}
case 0:
printf("退出 NUMA 页面管理工具\n");
return;
default:
printf("无效选择\n");
break;
}
}
}
void demonstrate_move_pages_api() {
printf("=== move_pages API 演示 ===\n");
if (numa_available() == -1) {
printf("系统不支持 NUMA\n");
return;
}
printf("move_pages 函数原型:\n");
printf("long move_pages(int pid, unsigned long count,\n");
printf(" void **pages, const int *nodes,\n");
printf(" int *status, int flags);\n");
printf("\n参数说明:\n");
printf(" pid: 进程ID (0表示当前进程)\n");
printf(" count: 页面数量\n");
printf(" pages: 页面地址数组\n");
printf(" nodes: 目标节点数组\n");
printf(" status: 状态返回数组\n");
printf(" flags: 控制标志\n");
printf(" MPOL_MF_MOVE: 移动页面\n");
printf(" MPOL_MF_MOVE_ALL: 移动所有页面\n");
printf("\n返回值:\n");
printf(" 0: 成功\n");
printf(" -1: 失败,并设置 errno\n");
}
#else
void interactive_numa_page_manager() {
printf("=== NUMA 页面管理工具 ===\n");
printf("编译时未启用 NUMA 支持\n");
printf("功能受限\n");
}
void demonstrate_move_pages_api() {
printf("=== move_pages API 演示 ===\n");
printf("编译时未启用 NUMA 支持\n");
}
#endif
int main() {
printf("=== NUMA 页面管理工具 ===\n");
#ifdef HAVE_NUMA
if (numa_available() == -1) {
printf("警告: 系统支持 NUMA 但当前不可用\n");
} else {
printf("✓ NUMA 支持可用\n");
}
#else
printf("ℹ 编译时未启用 NUMA 支持\n");
#endif
// 演示 API
demonstrate_move_pages_api();
// 启动交互式工具
char choice;
printf("\n是否启动交互式 NUMA 页面管理工具? (y/N): ");
if (scanf(" %c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
interactive_numa_page_manager();
}
return 0;
}
9. move_pages 标志说明 链接到标题
#ifdef HAVE_NUMA
// move_pages 支持的标志:
// MPOL_MF_MOVE
// • 移动指定的页面
// • 不移动已修改的页面(脏页)
// • 相对安全的移动方式
// MPOL_MF_MOVE_ALL
// • 移动所有指定的页面
// • 包括已修改的页面
// • 可能导致数据丢失风险
// 使用示例:
void example_move_flags() {
void *pages[1];
int nodes[1];
int status[1];
// 安全移动
move_pages(0, 1, pages, nodes, status, MPOL_MF_MOVE);
// 强制移动(谨慎使用)
move_pages(0, 1, pages, nodes, status, MPOL_MF_MOVE_ALL);
}
#endif
10. 实际应用场景 链接到标题
场景1:NUMA 感知的应用程序 链接到标题
#ifdef HAVE_NUMA
void numa_aware_page_management() {
// 获取当前线程的 NUMA 节点
int current_cpu = sched_getcpu();
int current_node = numa_node_of_cpu(current_cpu);
// 分配内存
size_t page_size = getpagesize();
void *memory = mmap(NULL, 4 * page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// 将页面移动到当前节点以优化访问
void *pages[4];
int target_nodes[4];
int status[4];
for (int i = 0; i < 4; i++) {
pages[i] = (char*)memory + i * page_size;
target_nodes[i] = current_node;
}
move_pages(0, 4, pages, target_nodes, status, MPOL_MF_MOVE);
// 使用内存...
munmap(memory, 4 * page_size);
}
#endif
场景2:数据库系统优化 链接到标题
#ifdef HAVE_NUMA
void database_numa_optimization() {
// 为不同数据库缓冲池分配不同的 NUMA 节点
int num_nodes = numa_num_configured_nodes();
// 分配缓冲池内存
size_t buffer_pool_size = 1024 * 1024 * 1024; // 1GB
void *buffer_pool = numa_alloc_local(buffer_pool_size);
// 将缓冲池页面分布到多个节点以平衡负载
size_t page_size = getpagesize();
int page_count = buffer_pool_size / page_size;
void **pages = malloc(page_count * sizeof(void*));
int *nodes = malloc(page_count * sizeof(int));
int *status = malloc(page_count * sizeof(int));
for (int i = 0; i < page_count; i++) {
pages[i] = (char*)buffer_pool + i * page_size;
nodes[i] = i % num_nodes; // 轮转分配到不同节点
}
move_pages(0, page_count, pages, nodes, status, MPOL_MF_MOVE);
// 清理
free(pages);
free(nodes);
free(status);
numa_free(buffer_pool, buffer_pool_size);
}
#endif
场景3:高性能计算 链接到标题
#ifdef HAVE_NUMA
void hpc_numa_placement() {
// 根据线程拓扑优化页面位置
int thread_id = 0; // 应从线程库获取
int node_count = numa_num_configured_nodes();
int target_node = thread_id % node_count;
// 分配工作内存
size_t work_memory_size = 64 * 1024 * 1024; // 64MB
void *work_memory = numa_alloc_onnode(work_memory_size, target_node);
// 确保所有页面都在目标节点
size_t page_size = getpagesize();
int page_count = work_memory_size / page_size;
void **pages = malloc(page_count * sizeof(void*));
int *nodes = malloc(page_count * sizeof(int));
int *status = malloc(page_count * sizeof(int));
for (int i = 0; i < page_count; i++) {
pages[i] = (char*)work_memory + i * page_size;
nodes[i] = target_node;
}
move_pages(0, page_count, pages, nodes, status, MPOL_MF_MOVE);
// 执行计算...
// 清理
free(pages);
free(nodes);
free(status);
numa_free(work_memory, work_memory_size);
}
#endif
11. 注意事项 链接到标题
使用 move_pages
时需要注意:
- 内核版本: 需要支持 NUMA 的内核版本(2.6.18+)
- 权限要求: 移动其他进程需要适当权限
- 内存锁定: 锁定的页面可能无法移动
- 性能影响: 页面移动可能暂时影响性能
- 硬件支持: 需要真正的 NUMA 硬件
- 数据一致性: 确保移动过程中数据一致性
12. 系统配置检查 链接到标题
# 检查 NUMA 支持
numactl --hardware
# 显示 NUMA 策略
numactl --show
# 查看 NUMA 统计信息
numastat
# 检查内核 NUMA 支持
grep -i numa /boot/config-$(uname -r)
# 查看进程 NUMA 信息
cat /proc/self/numa_maps
# 检查 NUMA 节点内存使用
cat /sys/devices/system/node/node*/meminfo
# 查看系统调用支持
ausyscall move_pages
13. 编译和链接 链接到标题
# 安装 NUMA 开发库
# Ubuntu/Debian: sudo apt-get install libnuma-dev
# CentOS/RHEL: sudo yum install numactl-devel
# 编译支持 NUMA 的程序
gcc -DHAVE_NUMA -lnuma program.c -o program
# 运行 NUMA 感知程序
numactl --cpunodebind=0 --membind=0 ./program
总结 链接到标题
move_pages
是 Linux 系统中用于精确 NUMA 内存管理的重要系统调用:
关键特性:
- 精确控制: 可以指定哪些页面移动到哪个节点
- 细粒度: 以页面为单位进行操作
- 进程控制: 可以操作指定进程的页面
- 状态反馈: 提供详细的操作状态信息
主要应用:
- 高性能计算和科学计算
- 数据库系统优化
- 内存密集型应用程序
- 系统管理和监控工具
使用要点:
- 检查系统 NUMA 支持
- 理解 NUMA 拓扑结构
- 合理使用移动标志
- 监控移动效果和性能
- 注意数据一致性和安全性
正确使用 move_pages
可以显著提升 NUMA 系统的内存访问性能,是现代多核系统优化的重要工具。