YashanDB 启动失败 YAS-00101内存分配错误:根因分析与解决
摘要:本文分析YashanDB启动报错「YAS-00101 cannot allocate 67108864 bytes」的根源问题。通过诊断发现,该错误并非物理内存不足导致,而是由于Linux系统在vm.overcommit_memory=2严格模式下,虚拟内存分配上限(CommitLimit)设置过低所致。文章详细介绍了Linux内存管理机制,包括虚拟内存分配的两个阶段、overcommit策
我们的文章会在微信公众号IT民工的龙马人生和博客网站 ( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢!
由于博客中有大量代码,通过页面浏览效果更佳。
摘要:本文从数据库运维与内核内存管理结合的角度,分析 YashanDB 启动时报错「YAS-00101 cannot allocate 67108864 bytes for analyze buffer」的完整诊断过程。问题根因是 Linux 的 vm.overcommit_memory=2 严格模式下,系统虚拟内存分配上限(CommitLimit)过低,与物理内存是否充足无关。文中给出根因说明、完整复现与验证步骤,以及面向数据库场景的配置建议。
一、问题现象
YashanDB 集群启动失败,任务状态为 CANCELED,返回码 1。错误信息如下:
[yashan@htz04 ~]$ yasboot cluster start -c yashandb
+------------------------------------------------------------------------------------------------------------+
| type | uuid | name | hostid | index | status | return_code | progress | cost |
+------------------------------------------------------------------------------------------------------------+
| task | 0a8f90292079cdf3 | StartYasdbCluster | - | yashandb | CANCELED | 1 | 100 | 17 |
+------+------------------+-------------------+--------+----------+----------+-------------+----------+------+
task completed, status: CANCELED
retcode: 1
stdout: node:1-1 start with ping failed
stderr: wait node 1-1 process start failed: Starting instance open
YAS-00101 cannot allocate 67108864 bytes for analyze buffer 2
Failed to start instance
要点:
- 错误码:YAS-00101
- 无法分配大小:67108864 字节(64MB),用途为 analyze buffer 2
- 实例在 open 阶段失败,属于启动期内存初始化问题
二、背景知识:Linux 虚拟内存与 overcommit
要理解「物理内存充足却分配失败」,需要区分 虚拟内存分配 与 物理内存使用。
2.1 内存分配的两个阶段
- 阶段一(虚拟内存):进程调用
malloc()/mmap()时,内核在进程的虚拟地址空间中预留一段区间,并计入系统的「已承诺分配」总量,此时通常尚未占用物理页。 - 阶段二(物理内存):进程首次访问该地址时发生缺页,内核才分配物理页并建立映射。
因此,限制往往作用在「虚拟内存承诺总量」上,与当前 free 显示的物理内存是否充足可以不一致。
2.2 vm.overcommit_memory 与 vm.overcommit_ratio
-
vm.overcommit_memory
- 0:启发式 overcommit(默认),允许一定程度的超分配。
- 1:总是允许 overcommit,可能引发后续 OOM。
- 2:严格模式,不允许超分配;系统对所有进程的虚拟内存「承诺总量」设一个硬上限。
-
vm.overcommit_ratio(仅在 overcommit_memory=2 时参与计算)
系统虚拟内存分配上限(CommitLimit)的计算公式为:
CommitLimit = Swap + (物理内存 × overcommit_ratio%)
单位一般为 KB。例如:Swap=2GB、物理内存=30GB、overcommit_ratio=50% 时:
- CommitLimit ≈ 2×1024×1024 + 30×1024×1024×0.5 ≈ 17.3e6 KB ≈ 16.5GB。
所有进程的虚拟内存分配总和(Committed_AS)不得超过 CommitLimit,否则新的 malloc/mmap 会失败并返回 ENOMEM(Cannot allocate memory),与当前物理内存是否空闲无关。
2.3 CommitLimit 与 Committed_AS
- CommitLimit:当前策略下,系统允许的「已承诺虚拟内存」总量上限(由 Swap、物理内存和 overcommit_ratio 决定)。
- Committed_AS:当前所有进程已承诺的虚拟内存总量(即已分配虚拟地址空间的总和)。
当 Committed_AS 接近或达到 CommitLimit 时,新的虚拟内存分配就会失败。查看方式:
cat /proc/meminfo | grep -E 'CommitLimit|Committed_AS'
理解这两项,就能解释「free 显示内存很多,但数据库仍报无法分配 64MB」的现象。
三、诊断过程
3.1 初步环境检查:内存与 ulimit
物理内存与 Swap 均充足,ulimit 也未限制虚拟内存:
[yashan@htz04 ~]$ free -m
total used free shared buff/cache available
Mem: 30705 829 28297 16 1578 28310
Swap: 2083 0 2083
[yashan@htz04 ~]$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 122669
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 1048576
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1048576
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
结论:问题不在物理内存不足,也不在单进程 ulimit。
3.2 共享内存与 YashanDB 内存配置
共享内存参数足够大,且当时尚未创建共享段;YashanDB 配置显示需要较多内存(DATA_BUFFER、VM_BUFFER、SHARE_POOL 等合计约 14GB+):
[yashan@htz04 ~]$ sudo sysctl -qa|grep -Ei "kernel.shmmax|kernel.shmall"|grep -v permission
kernel.shmall = 3774873
kernel.shmmax = 246960619520
[yashan@htz04 config]$ grep "SIZE" $YASDB_DATA/config/yasdb.ini
DATA_BUFFER_SIZE = 10797M
VM_BUFFER_SIZE = 1378M
WORK_AREA_POOL_SIZE = 128M
MEX_POOL_SIZE = 128M
SHARE_POOL_SIZE = 1378M
[yashan@htz04 config]$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
3.3 内存碎片与 zone 信息
通过 buddyinfo 和 zoneinfo 确认空闲页充足,无严重碎片问题:
[17:50:29][yashan@htz04 ~]$ cat /proc/buddyinfo
[17:50:29]Node 0, zone DMA 2 1 1 2 2 1 1 1 1 1 236
[17:50:29]Node 0, zone Normal 39210 29174 10883 3277 1778 713 451 233 192 66 6444
[17:50:52][yashan@htz04 ~]$ cat /proc/zoneinfo | grep -A 5 -B 5 "pages free"
[17:50:52] nr_foll_pin_acquired 0
[17:50:52] nr_foll_pin_released 0
[17:50:52] nr_kernel_stack 4960
[17:50:52] nr_page_table_pages 889
[17:50:52] nr_swapcached 0
[17:50:52] pages free 242712
[17:50:52] min 4083
[17:50:52] low 5103
[17:50:52] high 6123
[17:50:52] spanned 524288
[17:50:52] present 524288
[17:50:52]--
[17:50:52] batch: 63
[17:50:52] vm stats threshold: 24
[17:50:52] node_unreclaimable: 0
[17:50:52] start_pfn: 524288
[17:50:52]Node 0, zone DMA32
[17:50:52] pages free 0
[17:50:52] min 0
[17:50:52] low 0
[17:50:52] high 0
[17:50:52] spanned 0
[17:50:52] present 0
[17:50:52] managed 0
[17:50:52] cma 0
[17:50:52] protection: (0, 0, 0, 0)
[17:50:52]Node 0, zone Normal
[17:50:52] pages free 6958480
[17:50:52] min 126988
[17:50:52] low 158735
[17:50:52] high 190482
[17:50:52] spanned 7787520
[17:50:52] present 7787520
[17:50:52]--
[17:50:52] batch: 63
[17:50:52] vm stats threshold: 54
[17:50:52] node_unreclaimable: 0
[17:50:52] start_pfn: 1048576
[17:50:52]Node 0, zone Movable
[17:50:52] pages free 0
[17:50:52] min 0
[17:50:52] low 0
[17:50:52] high 0
[17:50:52] spanned 0
[17:50:52] present 0
内存碎片统计脚本结果也表明可用大块内存充足:
[yashan@htz04 ~]$ sudo python3 mem_stats.py
============================================================
MEMORY FRAGMENTATION & MOVABLE STATISTICS
============================================================
ZONE | FRAG % | TOTAL FREE | HUGE AVAIL
-----------------------------------------------------------------
Node 0 - DMA | 0.2% | 948.09 MB | 946.00 MB
Node 0 - Normal | 5.1% | 26.73 GB | 25.38 GB
============================================================
MOVABLE MEMORY (FREE PAGES ONLY)
Definition: Physical RAM explicitly marked 'Movable' by kernel
allocator, available for compaction/migration.
------------------------------------------------------------
ZONE | MOVABLE PAGES | CAPACITY
------------------------------------------------------------
Node 0 - DMA | 245 | 944.59 MB
Node 0 - Normal | 84136 | 26.50 GB
Node of - type | 0 | 0 B
------------------------------------------------------------
TOTAL SYSTEM | | 27.42 GB
确认内存不存在碎片问题
至此可排除:物理内存不足、ulimit、共享内存参数、内存碎片。怀疑点转向 系统级虚拟内存承诺上限(overcommit)。
3.4 使用内存分配测试工具复现
为验证「虚拟内存承诺上限」导致分配失败,在服务器上使用自定义内存分配测试工具(见附录),按 64MB 块连续分配并 touch 内存,观察在约 16GB 处是否失败。
第一次运行(overcommit_ratio=50%,预期上限约 16.5GB):
[root@htz04 tmp]# /tmp/mem-alloc -s 20
╔════════════════════════════════════════════════════════════╗
║ 内存分配测试工具 v1.0 ║
╚════════════════════════════════════════════════════════════╝
=== 系统信息 ===
系统页大小: 4096 bytes (4.00 KB)
进程内存限制: 无限制 (RLIMIT_AS = INFINITY)
数据段限制: 无限制 (RLIMIT_DATA = INFINITY)
=== 系统内存信息 ===
物理内存总量: 29.99 GB
物理内存空闲: 27.63 GB
物理内存可用: 27.71 GB
Swap总量: 2.04 GB
Swap空闲: 2.04 GB
大页内存 (HugePages): 总计 0, 可用 0, 每页 2.00 MB
=== 内存Overcommit设置 ===
vm.overcommit_memory: 2 (严格 overcommit, 不允许超限)
vm.overcommit_ratio: 50%
警告: 此程序将分配 20.00 GB 内存
按Enter继续,或Ctrl+C取消...
=== 开始分配内存 ===
目标分配大小: 20.00 GB
使用大页内存: 否
初始化内存内容: 是
✓ 已分配: 1.00 GB
✓ 已分配: 2.00 GB
✓ 已分配: 3.00 GB
✓ 已分配: 4.00 GB
✓ 已分配: 5.00 GB
✓ 已分配: 6.00 GB
✓ 已分配: 7.00 GB
✓ 已分配: 8.00 GB
✓ 已分配: 9.00 GB
✓ 已分配: 10.00 GB
✓ 已分配: 11.00 GB
✓ 已分配: 12.00 GB
✓ 已分配: 13.00 GB
✓ 已分配: 14.00 GB
✓ 已分配: 15.00 GB
✓ 已分配: 16.00 GB
分配失败: chunk 256, 已分配 16.00 GB
错误: Cannot allocate memory (errno=12)
=== 分配结果 ===
实际分配: 16.00 GB / 20.00 GB (80.0%)
失败位置: chunk 256 (16.00 GB)
正在验证内存可访问性...
✓ chunk 0 可访问
✓ chunk 1 可访问
✓ chunk 2 可访问
✓ chunk 3 可访问
✓ chunk 4 可访问
✓ chunk 5 可访问
✓ chunk 6 可访问
✓ chunk 7 可访问
✓ chunk 8 可访问
✓ chunk 9 可访问
按Enter键释放内存并退出...
现象与 YashanDB 一致:在约 16GB 处出现 errno=12 (ENOMEM),且系统显示 overcommit 为严格模式、ratio=50%。
3.5 确认 CommitLimit 与 Committed_AS
直接查看内核提供的承诺量及上限,并核对与公式一致:
[yashan@htz04 tmp]$ cat /proc/meminfo|grep -E "CommitLimit|Committed_AS"
CommitLimit: 17301504 kB # 16.5GB(上限)
Committed_AS: 16818176 kB # 16.03GB(已分配)
[root@htz04 ~]# sysctl -qa|grep overcommit_memory
vm.overcommit_memory = 2
[root@htz04 ~]# sysctl -qa|grep overcommit_ratio
vm.overcommit_ratio = 50
手工计算与 CommitLimit 一致:
- 虚拟内存上限 = SWAP + (PHYSICAL × overcommit_ratio) = 2083 + (30705×0.5) = 17853952 KB(与 17301504 kB 量级一致,单位与舍入可能略有差异,以 /proc/meminfo 为准)。
根因确认:在 overcommit_memory=2、overcommit_ratio=50% 下,系统虚拟内存承诺上限约 16.5GB。YashanDB 启动时需要分配的虚拟地址空间(含 DATA_BUFFER、VM_BUFFER、SHARE_POOL 及 analyze buffer 等)超过该上限,导致在分配 64MB 的 analyze buffer 2 时触发 ENOMEM。
四、解决方案与验证
4.1 调整 overcommit_ratio
将 vm.overcommit_ratio 从 50% 提高到 80%,提高 CommitLimit,从而允许更多虚拟内存承诺:
解决方案:
[root@htz04 tmp]# sysctl -w vm.overcommit_ratio=80
vm.overcommit_ratio = 80
如需持久化,可写入 /etc/sysctl.conf 并执行 sysctl -p:
echo 'vm.overcommit_ratio = 80' >> /etc/sysctl.conf
sysctl -p
4.2 内存分配测试工具再次验证
在同一环境下再次运行 20GB 分配测试,应能完整分配并全部可访问:
验证内存是否可以分配。
[root@htz04 tmp]# ./mem-alloc -s 20
╔════════════════════════════════════════════════════════════╗
║ 内存分配测试工具 v1.0 ║
╚════════════════════════════════════════════════════════════╝
=== 系统信息 ===
系统页大小: 4096 bytes (4.00 KB)
进程内存限制: 无限制 (RLIMIT_AS = INFINITY)
数据段限制: 无限制 (RLIMIT_DATA = INFINITY)
=== 系统内存信息 ===
物理内存总量: 29.99 GB
物理内存空闲: 27.64 GB
物理内存可用: 27.72 GB
Swap总量: 2.04 GB
Swap空闲: 2.04 GB
大页内存 (HugePages): 总计 0, 可用 0, 每页 2.00 MB
=== 内存Overcommit设置 ===
vm.overcommit_memory: 2 (严格 overcommit, 不允许超限)
vm.overcommit_ratio: 80%
警告: 此程序将分配 20.00 GB 内存
按Enter继续,或Ctrl+C取消...
=== 开始分配内存 ===
目标分配大小: 20.00 GB
使用大页内存: 否
初始化内存内容: 是
✓ 已分配: 1.00 GB
✓ 已分配: 2.00 GB
✓ 已分配: 3.00 GB
✓ 已分配: 4.00 GB
✓ 已分配: 5.00 GB
✓ 已分配: 6.00 GB
✓ 已分配: 7.00 GB
✓ 已分配: 8.00 GB
✓ 已分配: 9.00 GB
✓ 已分配: 10.00 GB
✓ 已分配: 11.00 GB
✓ 已分配: 12.00 GB
✓ 已分配: 13.00 GB
✓ 已分配: 14.00 GB
✓ 已分配: 15.00 GB
✓ 已分配: 16.00 GB
✓ 已分配: 17.00 GB
✓ 已分配: 18.00 GB
✓ 已分配: 19.00 GB
✓ 已分配: 20.00 GB
=== 分配结果 ===
实际分配: 20.00 GB / 20.00 GB (100.0%)
正在验证内存可访问性...
✓ chunk 0 可访问
✓ chunk 1 可访问
✓ chunk 2 可访问
✓ chunk 3 可访问
✓ chunk 4 可访问
✓ chunk 5 可访问
✓ chunk 6 可访问
✓ chunk 7 可访问
✓ chunk 8 可访问
✓ chunk 9 可访问
按Enter键释放内存并退出...
正在释放内存...
内存已释放
说明:在 overcommit_ratio=80% 下,CommitLimit 约为 2GB + 30GB×80% ≈ 26GB,足以支持 20GB 的测试分配及 YashanDB 的启动期需求。
4.3 YashanDB 集群启动验证
以数据库运行用户启动集群,确认实例可正常打开并运行:
验证数据库是否可以启动
[root@htz04 tmp]# su - yashan -c "yasboot cluster start -c yashandb"
+------------------------------------------------------------------------------------------------------------+
| type | uuid | name | hostid | index | status | return_code | progress | cost |
+------------------------------------------------------------------------------------------------------------+
| task | 62b029a507cb3961 | StartYasdbCluster | - | yashandb | SUCCESS | 0 | 100 | 18812 |
+------+------------------+-------------------+--------+----------+---------+-------------+----------+-------+
task completed, status: SUCCESS
[root@htz04 tmp]# ps -ef|grep yasdb
yashan 1459 1 0 Feb06 ? 00:09:01 /home/yashan/.yasboot/yashandb_yasdb_home/om/bin/monit -c /home/yashan/.yasboot/yashandb_yasdb_home/om/monit/monitrc
yashan 1553 1 0 Feb06 ? 00:03:38 /data/yashan/yasdb_home/23.4.4.104/bin/yasom -c yashandb -l 10.10.10.130:1675 -d
yashan 1937 1 0 Feb06 ? 00:00:06 /data/yashan/yasdb_home/23.4.4.104/bin/yasagent -c yashandb -l 10.10.10.130:1676 -d
yashan 305783 1 5 18:44 ? 00:00:01 /data/yashan/yasdb_home/23.4.4.104/bin/yasdb open -D /data/yashan/yasdb_data/db-1-1
root 305886 299007 0 18:44 pts/2 00:00:00 grep --color=auto yasdb
集群状态为 SUCCESS,实例进程 yasdb open 已存在,说明 YAS-00101 已消除。
五、数据库场景下的配置建议
- 生产环境:若希望避免 OOM 且可预测内存上限,可保持 vm.overcommit_memory=2,并根据实例总 SGA/ 工作集设置 vm.overcommit_ratio(常见 70%–85%),使 CommitLimit 略大于「所有库实例 + 系统预留」的虚拟内存需求。
- 计算 CommitLimit:
CommitLimit ≈ Swap(KB) + MemTotal(KB)×(overcommit_ratio/100)。部署前可用cat /proc/meminfo与上述公式估算,或先用小工具(如文中 mem_alloc)压测到接近预期分配量。 - 替代方案:若不想提高 ratio,可 增加 Swap(例如增加 8GB Swap 文件并 swapon),同样会提高 CommitLimit;或二者结合(适度增加 Swap + 适度提高 ratio)。
- 监控:定期查看
Committed_AS与CommitLimit,避免长期接近上限导致偶发分配失败。
六、结论
- 现象:YashanDB 启动报 YAS-00101,无法分配 67108864 字节(64MB)给 analyze buffer 2。
- 根因:在 vm.overcommit_memory=2、vm.overcommit_ratio=50% 下,系统虚拟内存承诺上限约 16.5GB;YashanDB 启动期虚拟内存需求超过该上限,在分配 analyze buffer 时被内核拒绝(ENOMEM)。
- 解决:将 vm.overcommit_ratio 调整为 80%(或按需 70%–85%),必要时增加 Swap 或持久化 sysctl,使 CommitLimit 满足实例与系统总需求。
- 要点:此类「物理内存充足却无法分配」的问题,应优先检查 overcommit 策略 与 CommitLimit/Committed_AS,再结合 ulimit、共享内存、内存碎片等做完整排查。
附录:内存分配测试工具源码(完整)
以下为诊断过程中使用的内存分配测试工具完整源码,用于在目标环境中复现与验证虚拟内存上限行为。编译命令:gcc -o /tmp/mem_alloc /tmp/mem_alloc.c(或 gcc -o memory-test mem_alloc.c)。
/*
* 内存分配测试工具
* 用于诊断大内存分配失败的原因
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <errno.h>
#define CHUNK_SIZE (64 * 1024 * 1024) // 64MB per chunk
#define GB_SIZE (1024 * 1024 * 1024)
typedef struct {
void *ptr;
size_t size;
} MemoryChunk;
void print_system_info() {
printf("=== 系统信息 ===\n");
// 获取页大小
long page_size = sysconf(_SC_PAGESIZE);
printf("系统页大小: %ld bytes (%.2f KB)\n", page_size, page_size / 1024.0);
// 获取进程内存限制
struct rlimit rlim;
if (getrlimit(RLIMIT_AS, &rlim) == 0) {
if (rlim.rlim_cur == RLIM_INFINITY) {
printf("进程内存限制: 无限制 (RLIMIT_AS = INFINITY)\n");
} else {
printf("进程内存限制: %.2f GB (RLIMIT_AS = %lu)\n",
rlim.rlim_cur / (double)GB_SIZE, rlim.rlim_cur);
}
}
if (getrlimit(RLIMIT_DATA, &rlim) == 0) {
if (rlim.rlim_cur == RLIM_INFINITY) {
printf("数据段限制: 无限制 (RLIMIT_DATA = INFINITY)\n");
} else {
printf("数据段限制: %.2f GB (RLIMIT_DATA = %lu)\n",
rlim.rlim_cur / (double)GB_SIZE, rlim.rlim_cur);
}
}
printf("\n");
}
void read_overcommit_settings() {
printf("=== 内存Overcommit设置 ===\n");
FILE *fp;
int value;
// vm.overcommit_memory
fp = fopen("/proc/sys/vm/overcommit_memory", "r");
if (fp) {
if (fscanf(fp, "%d", &value) == 1) {
printf("vm.overcommit_memory: %d ", value);
switch (value) {
case 0:
printf("(启发式 overcommit)\n");
break;
case 1:
printf("(总是 overcommit)\n");
break;
case 2:
printf("(严格 overcommit, 不允许超限)\n");
break;
}
}
fclose(fp);
}
// vm.overcommit_ratio
fp = fopen("/proc/sys/vm/overcommit_ratio", "r");
if (fp) {
if (fscanf(fp, "%d", &value) == 1) {
printf("vm.overcommit_ratio: %d%%\n", value);
}
fclose(fp);
}
printf("\n");
}
void read_memory_info() {
printf("=== 系统内存信息 ===\n");
FILE *fp = fopen("/proc/meminfo", "r");
if (!fp) {
printf("无法读取 /proc/meminfo\n");
return;
}
char line[256];
unsigned long mem_total = 0, mem_free = 0, mem_available = 0;
unsigned long swap_total = 0, swap_free = 0;
unsigned long hugepages_total = 0, hugepages_free = 0, hugepagesize = 0;
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "MemTotal:", 9) == 0) {
sscanf(line, "MemTotal: %lu kB", &mem_total);
} else if (strncmp(line, "MemFree:", 8) == 0) {
sscanf(line, "MemFree: %lu kB", &mem_free);
} else if (strncmp(line, "MemAvailable:", 13) == 0) {
sscanf(line, "MemAvailable: %lu kB", &mem_available);
} else if (strncmp(line, "SwapTotal:", 10) == 0) {
sscanf(line, "SwapTotal: %lu kB", &swap_total);
} else if (strncmp(line, "SwapFree:", 9) == 0) {
sscanf(line, "SwapFree: %lu kB", &swap_free);
} else if (strncmp(line, "HugePages_Total:", 16) == 0) {
sscanf(line, "HugePages_Total: %lu", &hugepages_total);
} else if (strncmp(line, "HugePages_Free:", 15) == 0) {
sscanf(line, "HugePages_Free: %lu", &hugepages_free);
} else if (strncmp(line, "Hugepagesize:", 13) == 0) {
sscanf(line, "Hugepagesize: %lu", &hugepagesize);
}
}
fclose(fp);
printf("物理内存总量: %.2f GB\n", mem_total / 1024.0 / 1024.0);
printf("物理内存空闲: %.2f GB\n", mem_free / 1024.0 / 1024.0);
printf("物理内存可用: %.2f GB\n", mem_available / 1024.0 / 1024.0);
printf("Swap总量: %.2f GB\n", swap_total / 1024.0 / 1024.0);
printf("Swap空闲: %.2f GB\n", swap_free / 1024.0 / 1024.0);
printf("大页内存 (HugePages): 总计 %lu, 可用 %lu, 每页 %.2f MB\n",
hugepages_total, hugepages_free, hugepagesize / 1024.0);
printf("\n");
}
void allocate_memory(size_t total_size_gb, int use_hugepages, int touch_memory) {
printf("=== 开始分配内存 ===\n");
printf("目标分配大小: %.2f GB\n", total_size_gb / (double)GB_SIZE);
printf("使用大页内存: %s\n", use_hugepages ? "是" : "否");
printf("初始化内存内容: %s\n", touch_memory ? "是" : "否");
printf("\n");
size_t chunk_size = use_hugepages ? (2 * 1024 * 1024) : CHUNK_SIZE;
size_t total_chunks = (total_size_gb / (double)chunk_size);
MemoryChunk *chunks = malloc(sizeof(MemoryChunk) * (total_chunks + 1));
if (!chunks) {
printf("无法分配chunk数组内存\n");
return;
}
size_t allocated = 0;
size_t failed_at = 0;
for (size_t i = 0; i < total_chunks; i++) {
void *ptr;
if (use_hugepages) {
// 使用mmap分配大页内存
ptr = mmap(NULL, chunk_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
-1, 0);
} else {
// 使用malloc分配普通内存
ptr = malloc(chunk_size);
}
if (!ptr) {
failed_at = i;
printf("分配失败: chunk %lu, 已分配 %.2f GB\n",
i, (i * chunk_size) / (double)GB_SIZE);
printf(" 错误: %s (errno=%d)\n", strerror(errno), errno);
break;
}
chunks[i].ptr = ptr;
chunks[i].size = chunk_size;
allocated = (i + 1) * chunk_size;
// 初始化内存内容(如果请求)
if (touch_memory) {
memset(ptr, 0xAB, chunk_size);
}
// 每分配1GB输出一次进度
if ((i + 1) % (1024 * 1024 * 1024 / chunk_size) == 0) {
printf("✓ 已分配: %.2f GB\n", allocated / (double)GB_SIZE);
}
}
printf("\n");
printf("=== 分配结果 ===\n");
printf("实际分配: %.2f GB / %.2f GB (%.1f%%)\n",
allocated / (double)GB_SIZE,
total_size_gb / (double)GB_SIZE,
(allocated * 100.0) / total_size_gb);
if (failed_at > 0) {
printf("失败位置: chunk %lu (%.2f GB)\n", failed_at,
(failed_at * chunk_size) / (double)GB_SIZE);
}
// 验证内存是否可访问
if (allocated > 0) {
printf("\n正在验证内存可访问性...\n");
size_t verify_chunks = (allocated > chunk_size * 10) ? 10 : (allocated / chunk_size);
for (size_t i = 0; i < verify_chunks; i++) {
if (chunks[i].ptr) {
if (!touch_memory) {
// 如果之前没有初始化,现在写入测试
memset(chunks[i].ptr, 0x5A, chunk_size);
}
// 读取验证
volatile char *p = (volatile char *)chunks[i].ptr;
char test = p[0];
printf("✓ chunk %lu 可访问\n", i);
}
}
}
printf("\n按Enter键释放内存并退出...");
getchar();
// 释放内存
printf("\n正在释放内存...\n");
for (size_t i = 0; i < total_chunks; i++) {
if (chunks[i].ptr) {
if (use_hugepages) {
munmap(chunks[i].ptr, chunks[i].size);
} else {
free(chunks[i].ptr);
}
}
}
free(chunks);
printf("内存已释放\n");
}
void print_help() {
printf("内存分配测试工具\n\n");
printf("用法: %s [选项]\n\n", "mem-alloc");
printf("选项:\n");
printf(" -s SIZE 分配的内存大小 (GB), 例如: -s 20\n");
printf(" -n 不初始化内存内容 (只分配不访问)\n");
printf(" -h 使用大页内存 (HugePages)\n");
printf(" -i 仅显示系统信息,不分配内存\n");
printf(" -help 显示此帮助信息\n\n");
printf("示例:\n");
printf(" %s -s 20 # 分配20GB普通内存\n", "mem-alloc");
printf(" %s -s 20 -n # 分配20GB不初始化\n", "mem-alloc");
printf(" %s -s 20 -h # 分配20GB大页内存\n", "mem-alloc");
printf(" %s -i # 仅显示系统信息\n", "mem-alloc");
}
int main(int argc, char *argv[]) {
int use_hugepages = 0;
int touch_memory = 1;
int info_only = 0;
size_t target_size_gb = (size_t)15 * GB_SIZE; // 默认15GB
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
i++;
target_size_gb = (size_t)(atof(argv[i]) * GB_SIZE);
} else if (strcmp(argv[i], "-n") == 0) {
touch_memory = 0;
} else if (strcmp(argv[i], "-h") == 0) {
use_hugepages = 1;
} else if (strcmp(argv[i], "-i") == 0) {
info_only = 1;
} else if (strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0) {
print_help();
return 0;
} else {
printf("未知选项: %s\n", argv[i]);
print_help();
return 1;
}
}
printf("╔════════════════════════════════════════════════════════════╗\n");
printf("║ 内存分配测试工具 v1.0 ║\n");
printf("╚════════════════════════════════════════════════════════════╝\n\n");
print_system_info();
read_memory_info();
read_overcommit_settings();
if (info_only) {
printf("仅显示系统信息,按Enter退出...\n");
getchar();
return 0;
}
printf("警告: 此程序将分配 %.2f GB 内存\n", target_size_gb / (double)GB_SIZE);
printf("按Enter继续,或Ctrl+C取消...\n");
getchar();
allocate_memory(target_size_gb, use_hugepages, touch_memory);
return 0;
}
生成与编译命令(与文档中一致):
cat > /tmp/mem_alloc.c << 'HTZ'
# (将上述源码粘贴在此处)
HTZ
gcc -o /tmp/mem_alloc /tmp/mem_alloc.c
用法说明(与文档中一致):
内存分配测试工具
用法: mem-alloc [选项]
选项:
-s SIZE 分配的内存大小 (GB), 例如: -s 20
-n 不初始化内存内容 (只分配不访问)
-h 使用大页内存 (HugePages)
-i 仅显示系统信息,不分配内存
-help 显示此帮助信息
示例:
mem-alloc -s 20 # 分配20GB普通内存
mem-alloc -s 20 -n # 分配20GB不初始化
mem-alloc -s 20 -h # 分配20GB大页内存
mem-alloc -i # 仅显示系统信息
昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链
更多推荐



所有评论(0)