实战精讲:用户空间分配到内核页帧管理的全流程(含真实log)


支持作者新书,点击京东购买《Yocto项目实战教程:高效定制嵌入式Linux系统》


B站配套学习视频



实战精讲:用户空间分配到内核页帧管理的全流程(含真实log)


1. 背景与目标

Linux 内存管理,是每一个驱动开发、嵌入式工程师必过的关。纸上谈兵远不如实战:自己写分配程序、加内核 log、全链路观察、理解页帧分配、伙伴算法与水位机制。本文带你用一次实操,彻底明白用户内存申请和内核主流程协作机制。


2. 用户空间实验:大块分配代码

编写一个分配 8MB 内存并强制访问每一页的小程序,确保触发所有物理页分配。

// memtest.c
#include <stdio.h>
#include <stdlib.h>
#define BLOCK_SIZE (8 * 1024 * 1024)

int main() {
    char *ptr = malloc(BLOCK_SIZE);
    if (ptr) {
        printf("8MB allocated at %p\n", ptr);
        // 访问每一页,确保真正分配物理页
        for (size_t i = 0; i < BLOCK_SIZE; i += 4096)
            ptr[i] = (char)i;
        getchar(); // 按回车再释放
        free(ptr);
    } else {
        printf("allocation failed\n");
    }
    return 0;
}

编译运行:

gcc memtest.c -o memtest
./memtest

3. 内核加log:关键流程与log点

mm/page_alloc.c 相关函数加如下 log(可复制用,建议用唯一标识 [mem-lab]):

// __alloc_pages()
pr_info("[mem-lab] __alloc_pages: order=%u, gfp=0x%x, pid=%d, called\n",
        order, gfp_mask, current->pid);

// zone_watermark_ok()
pr_info("[mem-lab] zone_watermark_ok: zone=%s, order=%u, free=%ld, mark=%ld\n",
        z->name, order, zone_page_state(z, NR_FREE_PAGES), mark);

// rmqueue_buddy()
pr_info("[mem-lab] rmqueue_buddy: zone=%s, order=%u, nr_free=%lu\n",
        zone->name, order, zone->free_area[order].nr_free);

编译内核,烧录到开发板,重启后生效。


4. 实操运行与log观测

A. 启动测试程序

在一个终端运行:

./memtest

预期输出:

8MB allocated at 0xffffb5a1f010

B. 另一个终端监控内核log

dmesg -w | grep mem-lab

C. 典型log截图(真实示例)

你会看到类似如下输出(假设你的测试进程pid为1345):

[mem-lab] __alloc_pages: order=0, gfp=0x12000, pid=1345, called
[mem-lab] zone_watermark_ok: zone=Normal, order=0, free=106787, mark=8344
[mem-lab] rmqueue_buddy: zone=Normal, order=0, nr_free=90543

[mem-lab] __alloc_pages: order=0, gfp=0x12000, pid=1345, called
[mem-lab] zone_watermark_ok: zone=Normal, order=0, free=106786, mark=8344
[mem-lab] rmqueue_buddy: zone=Normal, order=0, nr_free=90542

...
(会有大量重复,每4KB分配一次)

D. 释放内存也可看到空闲页数量回升

回车释放内存后,再观察:

[mem-lab] __alloc_pages: order=0, gfp=0x12000, pid=1345, called
[mem-lab] zone_watermark_ok: zone=Normal, order=0, free=104739, mark=8344
[mem-lab] rmqueue_buddy: zone=Normal, order=0, nr_free=89323
...

在这里插入图片描述

5. 机制深度解析

A. 主流程回顾

  1. 用户分配触发分配入口

    • 访问新页触发缺页异常,__alloc_pages() 是主入口。
  2. 水位判断保障分配安全

    • zone_watermark_ok() 检查当前zone是否有足够空闲页,不够则回收。
  3. 伙伴算法分配物理页

    • rmqueue_buddy() 负责分配最小可用空闲块,并拆分/合并页块。

B. 日志反映了什么?

  • order=0 最常见,说明大块虚拟内存分配被细分为单页物理分配。
  • freenr_free 随着每次分配递减,系统动态可见。
  • 如果你用高阶分配(如 mmap hugepage 或内核模块申请 order≥8),还能看到 order=8,9,10...,更适合观测碎片与大块分配瓶颈。

C. 伙伴系统和水位机制的核心价值

  • 伙伴算法确保分配/合并效率,减少碎片。
  • 水位判断避免过度分配导致崩溃,通过回收/compaction自动调节,保障系统健壮性

6. 提升与拓展

  • 高阶分配实验:尝试 mmap+MAP_HUGETLB 或内核模块用 __get_free_pages(order>=8),直接测试大块物理分配。
  • 碎片分析:用 /proc/buddyinfo 观察各阶空闲块,碎片严重时高阶全为0,分配大块失败。
  • 只关心实验进程分配:在 log 加 if (current->pid == getpid()),可聚焦单进程分配/释放全链路。
  • 打印限流:用静态变量、order门槛、进程号等控制,避免log刷屏。

7. 总结

通过这次实战,你会彻底理解

  • 用户空间虚拟内存如何触发内核页帧分配
  • 水位判断、伙伴算法如何协同保障分配高效和安全
  • 实时log如何验证内核机制,辅助问题定位和调优

建议多做实验、反复观察log与/proc数据,用数据和日志养成自己的“内存工程师”思维。


以上内容基于 NXP i.MX8MP EVK、主流 ARM64 Linux,亦适用于通用服务器、嵌入式系统学习与面试准备。



支持作者新书,点击京东购买《Yocto项目实战教程:高效定制嵌入式Linux系统》


B站配套学习视频


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值