get_mempolicy系统调用及示例

get_mempolicy 函数详解

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

get_mempolicy 是 Linux 系统中用于查询 NUMA 内存分配策略的系统调用,可获取进程或指定内存区域的 NUMA 节点分配策略。该函数支持查询默认策略、特定地址策略及允许使用的 NUMA 节点集合,适用于优化多核 NUMA 架构下的内存访问性能。函数通过 mode 参数返回策略类型(如默认、优先节点、绑定节点等),nodemask 返回节点掩码,flags 控制查询行为。典型应用场景包括高性能计算和大内存应用,需配合 set_mempolicy 和 mbind 等函数使用。

https://www.calcguide.tech/2025/09/08/get-mempolicy系统调用及示例/

  1. 函数介绍

get_mempolicy 是 Linux 系统中用于获取内存策略(Memory Policy)的系统调用。可以把内存策略想象成”内存分配的导航系统”——它告诉操作系统应该将进程的内存分配到哪个 NUMA 节点上。

在现代多核系统中,特别是 NUMA(Non-Uniform Memory Access)架构中,不同的 CPU 核心访问不同内存节点的速度是不一样的。就像你去超市买东西,有些商品在你附近的货架上(访问快),有些在远处的货架上(访问慢)。内存策略就是告诉系统:“优先在我附近的内存区域分配内存”,从而提高程序性能。

get_mempolicy 允许你查询当前的内存分配策略,了解系统是如何为你的进程分配内存的。

  1. 函数原型
1
2
3
4
5
#include <numaif.h>

long get_mempolicy(int *mode, unsigned long *nodemask,
unsigned long maxnode, void *addr, unsigned long flags);

  1. 功能

get_mempolicy 函数用于获取当前进程或指定内存地址的内存分配策略。它可以查询:

  • 进程默认的内存策略

  • 特定内存地址的内存策略

  • 系统支持的 NUMA 节点信息

  1. 参数
  • mode: 指向整型变量的指针,用于存储返回的策略模式

  • nodemask: 指向节点掩码数组的指针,用于存储策略涉及的 NUMA 节点

  • maxnode: nodemask 数组的最大节点数

  • addr: 指定内存地址(可选,用于查询特定地址的策略)

  • flags: 控制操作行为的标志位

  1. 策略模式(mode 参数的可能值)

模式值说明MPOL_DEFAULT0默认策略,遵循系统默认行为MPOL_PREFERRED1优先分配到指定节点MPOL_BIND2严格绑定到指定节点集合MPOL_INTERLEAVE3在指定节点间交错分配MPOL_LOCAL4分配到本地节点(进程运行的节点)

  1. 标志位(flags 参数)

标志说明0查询进程默认策略MPOL_F_NODE返回节点信息MPOL_F_ADDR查询指定地址 addr 的策略MPOL_F_MEMS_ALLOWED返回进程允许使用的节点集合

  1. 返回值
  • 成功: 返回 0

  • 失败: 返回 -1,并设置相应的 errno 错误码

常见错误码:

  • EFAULT: 地址参数无效

  • EINVAL: 参数无效

  • ENOMEM: 内存不足

  1. 相似函数或关联函数
  • set_mempolicy: 设置内存策略

  • mbind: 为特定内存区域设置内存策略

  • getcpu: 获取当前 CPU 和 NUMA 节点信息

  • numa_ functions*: libnuma 库提供的 NUMA 操作函数

  • mbind: 绑定内存到特定节点

  1. 示例代码

示例1:基础用法 - 获取进程默认内存策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#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&#91;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&#91;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:查询特定内存地址的策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#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&#91;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 信息查询工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#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&#91;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&#91;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&#91;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&#91;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&#91;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;
}

编译和运行说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 编译示例程序(需要链接 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(如果需要)

1
2
3
4
5
6
7
8
9
# 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性能的系统调用

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

https://www.calcguide.tech/2025/09/08/get-mempolicy系统调用及示例/

data-ad-format="auto" data-full-width-responsive="true">