get_mempolicy系统调用及示例

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_DEFAULT0默认策略,遵循系统默认行为
MPOL_PREFERRED1优先分配到指定节点
MPOL_BIND2严格绑定到指定节点集合
MPOL_INTERLEAVE3在指定节点间交错分配
MPOL_LOCAL4分配到本地节点(进程运行的节点)

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

重要注意事项

  1. 系统要求: 需要 NUMA 支持的硬件和内核
  2. 库依赖: 通常需要链接 libnuma 库
  3. 权限: 一般不需要特殊权限
  4. 移植性: NUMA 功能是 Linux 特有的
  5. 性能影响: 查询内存策略本身开销很小

策略模式详解

  1. MPOL_DEFAULT: 使用系统默认的内存分配策略
  2. MPOL_PREFERRED: 优先在指定节点分配内存,如果该节点不可用则在其他节点分配
  3. MPOL_BIND: 严格限制只能在指定节点集合中分配内存
  4. MPOL_INTERLEAVE: 在多个节点间交错分配内存(适合大内存应用)
  5. MPOL_LOCAL: 总是在进程运行的本地节点分配内存

实际应用场景

  1. 高性能计算: 优化大规模数值计算的内存访问模式
  2. 数据库系统: 将数据分配到合适的 NUMA 节点以提高访问速度
  3. Web 服务器: 优化多线程应用的内存局部性
  4. 科学计算: 大型矩阵运算的内存布局优化
  5. 虚拟化环境: 为虚拟机合理分配物理内存节点

这些示例展示了 get_mempolicy 函数的各种使用方法,从基本的策略查询到完整的 NUMA 信息分析工具,帮助你理解和掌握 Linux NUMA 内存管理机制。

get_mempolicy系统调用, Linux get_mempolicy函数, get_mempolicy使用示例, 查询内存策略Linux, 理解get_mempolicy工作原理, Linux内存管理API, 系统调用与内存分配, 高级Linux编程技巧, get_mempolicy参数解析, 优化Linux性能的系统调用

get_mempolicy系统调用及示例-CSDN博客

此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注