linux 内核4.4 内存分配使用分析

现在想让某段内存"既不被分配,也不被线性映射",在 Linux 4.4 上应使用 memblock_remove()

对比维度 memblock_reserve memblock_remove
对 memblock.memory 的影响 保留在 memory 列表中 从 memory 列表中彻底删除
是否被线性映射 是,仍会建立 direct mapping 否,不进入内核线性映射
后续访问方式 可用 phys_to_virt() 必须用 ioremap()
适用场景 需要保留但不被分配的内存(如内核镜像) 不需要内核直接访问的内存(如固件区域)

第一步:启动早期从 memblock 中移除(放在 setup_arch 或早期 initcall)

复制代码
#include <linux/memblock.h>

#define MY_MEM_BASE  0x8600000
#define MY_MEM_SIZE  0x1000

static int __init my_board_mem_setup(void)
{
    /* 从 memblock.memory 中彻底移除该区域 */
    memblock_remove(MY_MEM_BASE, MY_MEM_SIZE);

    pr_info("my_board: removed phys [0x%lx-0x%lx] from kernel memory\n",
            (unsigned long)MY_MEM_BASE,
            (unsigned long)(MY_MEM_BASE + MY_MEM_SIZE - 1));
    return 0;
}
early_initcall(my_board_mem_setup);

第二步:在你的驱动中用 ioremap 映射并使用

复制代码
#include <linux/io.h>
#include <linux/module.h>

#define MY_MEM_BASE  0x8600000
#define MY_MEM_SIZE  0x1000

static void __iomem *my_mem;

static int __init my_driver_init(void)
{
    /* 动态创建映射,因为线性映射已不存在 */
    my_mem = ioremap(MY_MEM_BASE, MY_MEM_SIZE);
    if (!my_mem) {
        pr_err("my_driver: ioremap failed\n");
        return -ENOMEM;
    }

    /* 现在可以安全读写这段内存 */
    writel(0xdeadbeef, my_mem);
    pr_info("my_driver: read back: 0x%08x\n", readl(my_mem));

    return 0;
}

static void __exit my_driver_exit(void)
{
    iounmap(my_mem);
}

module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");

第一阶段:memblock 管理(boot-time)

此时还没有页表,也没有 struct page 数组。内核通过 memblock.memory 知道"哪些物理内存可用",通过 memblock.reserved 知道"哪些已被占用"。memblock_remove 的作用对象就是 memblock.memory------把它从"可用内存"集合中删掉。

第二阶段:paging_init 建立线性映射

paging_init 遍历 memblock.memory 中的区域,为每一个区域创建页表项,建立内核线性映射(direct mapping)。只有还在 memblock.memory 里的区域才会被映射。 被 memblock_remove 移除的区域,页表创建时根本不会看到它。

时序要求:

复制代码
early_initcall(my_board_reserve_mem)   ← 必须在这里完成 remove
        ↓
paging_init()                           ← 遍历 memblock.memory 建立页表
        ↓
内存分配器初始化                         ← 基于 memblock.memory 构建可用内存池

如果在 paging_init 之后再调用 memblock_remove,页表已经建好,线性映射已经存在,再移除 memblock.memory 也无法撤销已建立的映射。那段物理内存仍然可以通过 phys_to_virt() 被访问,失去了"不被线性映射"的意义。

MIPS 架构的 boot_memtype 枚举定义在 arch/mips/include/asm/bootinfo.h 中,主要包含以下几类:

枚举值 含义 说明
BOOT_MEM_RAM 可用 RAM 内核可以分配和使用的普通内存,是最常见的类型
BOOT_MEM_ROM_DATA ROM 数据区域 存放只读固件数据的区域,内核通常只读访问
BOOT_MEM_RESERVED 保留内存 已被占用或专门保留、内核不可使用的区域
BOOT_MEM_NOMAP 保留但不映射 保留内存且不建立线性映射(较新内核版本支持)

BOOT_MEM_NOMAP 是在 BOOT_MEM_RESERVED 基础上进一步细分的类型,与之前讨论的 memblock_mark_nomap() 语义对应,表示"内核不仅不能分配,也不应建立线性映射"。

add_memory_region函数

复制代码
void __init add_memory_region(phys_addr_t start, phys_addr_t size, long type);

/* arch/mips/your_board/setup.c */
void __init prom_init_memory(void)
{
    /* 注册 0x10000000 开始的 256MB 为可用 RAM */
    add_memory_region(0x10000000, 0x10000000, BOOT_MEM_RAM);

    /* 注册 PCI MMIO 区域为保留 */
    add_memory_region(LOONGSON_PCI_MEM_START,
                      LOONGSON_PCI_MEM_END - LOONGSON_PCI_MEM_START,
                      BOOT_MEM_RESERVED);
}

5.10 内核可以使用memblock_mark_nomap/memblock_reserve 函数替换 ,并且在设备树上面加上no-map。

相关推荐
Rabitebla20 小时前
【Linux 底层原理】进程(十):进程等待详解---从僵尸进程到 wait、waitpid 与 status
linux·c++·笔记·学习·visual studio
Decay__20 小时前
18_MySQL 智能巡检方案 —— 巡检项清单 / 一键巡检脚本 / 健康评分
运维·数据库·sql·mysql·架构
天远API1 天前
零信任架构实战:基于天远运营商三要素详版V即时版构建自动化消费电子大促电商平台高价值权益秒杀与优惠券防羊毛高并发实名核验网关
运维·人工智能·架构·自动化
Android系统攻城狮1 天前
Linux FFmpeg深度解析之avcodec_default_get_encode_buffer调用流程与实战(二十八)
linux·运维·服务器·ffmpeg·车载音视频·ffmpeg音视频
程序员初一1 天前
8080 打不开的四层定位:监听地址、ufw、DOCKER 链、安全组
linux·ssh
程序开发阿元1 天前
swappiness 60 和 10 差在哪:一台 1C1G 的三轮对比
linux·ssh
无足鸟ICT1 天前
【RHCA+】mpstat命令(监控CPU)
linux
乐维_lwops1 天前
案例解读:大型综合性科创产业园运维监控落地实践
运维·网络·人工智能·安全·it监控·国产运维智能体
GeW1 天前
新质生产力Linux和数据库为啥这么关键
linux
番茄啊1 天前
linux 开启xftp连接
linux·运维·服务器