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。

相关推荐
科技象限1 小时前
电脑防拍照系统:屏幕被手机拍走,还能追得回来吗
运维·网络安全·电脑·安企神
燕落南枝1 小时前
C 语言学习大总结:从入门到进阶的完整知识体系
服务器·c语言·学习
开开心心就好1 小时前
打印机卡住打不了?一键清理卡死打印队列
服务器·开发语言·算法·pdf·ocr·idea·visual studio code
茉莉玫瑰花茶2 小时前
GO [ 数据格式 ]
服务器·网络·算法·golang
欢乐的小猪2 小时前
Linux命令 chmod
linux·运维·服务器
silvamux_2 小时前
MCP 协议如何让大模型完成工具调用
大数据·服务器·elasticsearch·fastapi·vllm
guo_wen_qiang2 小时前
云服务器安装MongoDB-资源包安装
运维·服务器·mongodb
Happy_Sharing2 小时前
Windows虚拟机安装ubuntu后,如何在虚拟机Ubuntu系统中访问Windows文件夹
linux·运维·ubuntu
9144062322 小时前
安装jenkins遇到的问题总结
运维·jenkins