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: 目标进程的进程 ID
    • 0: 表示当前进程
    • 正整数: 指定具体进程的 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(&current_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(&current_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 时需要注意:

  1. 内核版本: 需要支持 NUMA 的内核版本(2.6.18+)
  2. 权限要求: 移动其他进程需要适当权限
  3. 内存锁定: 锁定的页面可能无法移动
  4. 性能影响: 页面移动可能暂时影响性能
  5. 硬件支持: 需要真正的 NUMA 硬件
  6. 数据一致性: 确保移动过程中数据一致性

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 内存管理的重要系统调用:

关键特性:

  1. 精确控制: 可以指定哪些页面移动到哪个节点
  2. 细粒度: 以页面为单位进行操作
  3. 进程控制: 可以操作指定进程的页面
  4. 状态反馈: 提供详细的操作状态信息

主要应用:

  1. 高性能计算和科学计算
  2. 数据库系统优化
  3. 内存密集型应用程序
  4. 系统管理和监控工具

使用要点:

  1. 检查系统 NUMA 支持
  2. 理解 NUMA 拓扑结构
  3. 合理使用移动标志
  4. 监控移动效果和性能
  5. 注意数据一致性和安全性

正确使用 move_pages 可以显著提升 NUMA 系统的内存访问性能,是现代多核系统优化的重要工具。