我们的文章会在微信公众号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_ASCommitLimit,避免长期接近上限导致偶发分配失败。

六、结论

  • 现象:YashanDB 启动报 YAS-00101,无法分配 67108864 字节(64MB)给 analyze buffer 2。
  • 根因:在 vm.overcommit_memory=2vm.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                       # 仅显示系统信息

Logo

昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链

更多推荐