[TOC]
❓ 问题描述:
linux内核驱动中预留内存一般分为下面几种情况:
1.通用性CMA(当设备驱动程序不使用时,系统可以调度给其他程序使用,当设备驱动使用时,系统再根据必定策略分配给设备驱动)
2.专用性CMA(只给专用的设备驱动使用,系统不可以给其他程序使用)
3.早期预留内存(预留一部分内存,和专用型CMA基本一样)
4.普通预留内存(可以用户自己使用,使用完成后可以再放到系统中,让系统给其他程序使用)
5./memreserve/的使用
6.uboot中传递参数,给物理内存进行分段
日志
添加打印日志信息
分析步骤
第1步:
第2步:
...
代码片段
- 通用性CMA
定义cma区域有2种方式:第1种在设备树中定义;第2种在.config中定义:例如定义48M的cma区域CONFIG_CMA_SIZE_MBYTES=48:其中设备树中定义的会覆盖.config定义的信息
设备树信息:
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
reserved: cma@70000000 {
compatible = "shared-dma-pool";
reusable;
reg = <0x70000000 0x6400000>; //定义cma区域的地址和大小
//size = <0x6400000>; //也可以只定义cma区域的大小,不定义起始地址,系统会自己根据实际情况去选择
alignment = <0x2000>;
linux,cma-default;
};
};
reserved-driver@0 {
compatible = "linux,virtual-free";
memory-region = <&reserved>;
};


#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/slab.h>
// 定义内存大小 (10MB 和 50MB)
#define MEM_SIZE_10M (10 * 1024 * 1024) // 10MB
#define MEM_SIZE_50M (50 * 1024 * 1024) // 50MB
// 内存块结构体
struct dma_memory_block {
void *virt_addr; // 内核虚拟地址
dma_addr_t dma_addr; // DMA总线地址
size_t size; // 内存大小
char *name; // 内存块名称
};
// 设备私有数据
struct dma_dev_priv {
struct dma_memory_block block1; // 10MB内存块
struct dma_memory_block block2; // 50MB内存块
};
// 测试数据写入和读取函数
static void test_memory_access(struct dma_memory_block *block)
{
u32 test_value = 0x12345678;
u32 read_back;
size_t test_offset = 0x100; // 测试偏移位置
// 简单测试:写入一个32位值
*(u32 *)(block->virt_addr + test_offset) = test_value;
read_back = *(u32 *)(block->virt_addr + test_offset);
dev_info(block->name ? NULL : NULL,
"Memory block '%s' test:
"
" Wrote value: 0x%x at offset 0x%zx
"
" Read value: 0x%x
",
block->name, test_value, test_offset, read_back);
// 块测试:写入一段数据
memset(block->virt_addr, 0xAA, 1024); // 写入前1KB
if (*(u8 *)block->virt_addr == 0xAA &&
*(u8 *)(block->virt_addr + 1023) == 0xAA) {
dev_info(block->name ? NULL : NULL,
" Block write test passed for first 1KB
");
} else {
dev_err(block->name ? NULL : NULL,
" Block write test failed!
");
}
}
static int dma_mem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dma_dev_priv *priv;
int ret = 0;
// 分配设备私有数据
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
// 初始化内存块信息
priv->block1.size = MEM_SIZE_10M;
priv->block1.name = "10MB Block";
priv->block2.size = MEM_SIZE_50M;
priv->block2.name = "50MB Block";
// 分配第一块内存 (10MB)
priv->block1.virt_addr = dma_alloc_coherent(dev,
priv->block1.size,
&priv->block1.dma_addr,
GFP_KERNEL);
if (!priv->block1.virt_addr) {
dev_err(dev, "Failed to allocate 10MB DMA memory
");
return -ENOMEM;
}
// 分配第二块内存 (50MB)
priv->block2.virt_addr = dma_alloc_coherent(dev,
priv->block2.size,
&priv->block2.dma_addr,
GFP_KERNEL);
if (!priv->block2.virt_addr) {
dev_err(dev, "Failed to allocate 50MB DMA memory
");
ret = -ENOMEM;
goto free_block1;
}
// 打印第一块内存信息
dev_info(dev, "
Allocated %s:
", priv->block1.name);
dev_info(dev, " Size: %zu bytes (%zu MB)
",
priv->block1.size, priv->block1.size / (1024 * 1024));
dev_info(dev, " Kernel virtual address: %p
", priv->block1.virt_addr);
dev_info(dev, " DMA bus address: %pad
", &priv->block1.dma_addr);
// 打印第二块内存信息
dev_info(dev, "
Allocated %s:
", priv->block2.name);
dev_info(dev, " Size: %zu bytes (%zu MB)
",
priv->block2.size, priv->block2.size / (1024 * 1024));
dev_info(dev, " Kernel virtual address: %p
", priv->block2.virt_addr);
dev_info(dev, " DMA bus address: %pad
", &priv->block2.dma_addr);
// 测试第一块内存的读写操作
dev_info(dev, "
Testing %s access...
", priv->block1.name);
test_memory_access(&priv->block1);
// 测试第二块内存的读写操作
dev_info(dev, "
Testing %s access...
", priv->block2.name);
test_memory_access(&priv->block2);
platform_set_drvdata(pdev, priv);
return 0;
free_block1:
dma_free_coherent(dev, priv->block1.size,
priv->block1.virt_addr, priv->block1.dma_addr);
return ret;
}
static int dma_mem_remove(struct platform_device *pdev)
{
struct dma_dev_priv *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
// 释放第二块内存
if (priv->block2.virt_addr) {
dma_free_coherent(dev, priv->block2.size,
priv->block2.virt_addr, priv->block2.dma_addr);
dev_info(dev, "Freed %s
", priv->block2.name);
}
// 释放第一块内存
if (priv->block1.virt_addr) {
dma_free_coherent(dev, priv->block1.size,
priv->block1.virt_addr, priv->block1.dma_addr);
dev_info(dev, "Freed %s
", priv->block1.name);
}
return 0;
}
static const struct of_device_id dma_mem_of_match[] = {
{ .compatible = "linux,virtual-free" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dma_mem_of_match);
static struct platform_driver dma_mem_driver = {
.probe = dma_mem_probe,
.remove = dma_mem_remove,
.driver = {
.name = "dma-two-blocks-driver",
.of_match_table = dma_mem_of_match,
},
};
module_platform_driver(dma_mem_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA Driver allocating two memory blocks (10MB and 50MB)");
MODULE_AUTHOR("Your Name");
内核输出信息(一般通过2种方式查看内存物理地址或者虚拟地址信息:1.内核启动信息 2.通过proc和sysfs文件系统方式)
可以看到分配的地址从0x70000000开始,大小为100M
Reserved memory: created CMA memory pool at 0x70000000, size 100 MiB
Reserved memory: initialized node cma@70000000, compatible id shared-dma-pool
//当设置为reusable时,可以看到物理内存区域没有变化(实际物理内存大小512M),同时被统计到了cma-reserved中
Memory: 409040K/524288K available (5461K kernel code, 170K rwdata, 1688K rodata, 296K init, 156K bss, 12848K reserved, 102400K cma-reserved)
//下面实际对应的是虚拟地址,而非物理地址,如果查看各个函数的虚拟地址,也可以查看cat /proc/kallsyms
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xffc00000 - 0xfff00000 (3072 kB)
vmalloc : 0xa0800000 - 0xff800000 (1520 MB)
lowmem : 0x80000000 - 0xa0000000 ( 512 MB)
modules : 0x7f000000 - 0x80000000 ( 16 MB)
.text : 0x80008000 - 0x80703a00 (7151 kB)
.init : 0x80704000 - 0x8074e000 ( 296 kB) //将来会进行释放
.data : 0x8074e000 - 0x80778aa0 ( 171 kB)
.bss : 0x8077b000 - 0x807a2038 ( 157 kB)
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
可以看到内核在启动后把init内存释放了
Freeing unused kernel memory: 296K
总的物理内存(524288K)
= 可用内存(409040K)
+ 普通预留内存(12848K,包含内核常驻内存(kernel code + rwdata + rodata + bss)和 其他预留内存)
+ CMA预留内存(102400K)

加载驱动输出信息:
可以看到分配的物理地址为0x70300000和0x70d00000,在上面定义的范围内
dma-two-blocks-driver reserved-driver@0:
Allocated 10MB Block:
dma-two-blocks-driver reserved-driver@0: Size: 10485760 bytes (10 MB)
dma-two-blocks-driver reserved-driver@0: Kernel virtual address: 90300000
dma-two-blocks-driver reserved-driver@0: DMA bus address: 0x70300000
dma-two-blocks-driver reserved-driver@0:
Allocated 50MB Block:
dma-two-blocks-driver reserved-driver@0: Size: 52428800 bytes (50 MB)
dma-two-blocks-driver reserved-driver@0: Kernel virtual address: 90d00000
dma-two-blocks-driver reserved-driver@0: DMA bus address: 0x70d00000
dma-two-blocks-driver reserved-driver@0:
Testing 10MB Block access...
(NULL device *): Memory block '10MB Block' test:
Wrote value: 0x12345678 at offset 0x100
Read value: 0x12345678
(NULL device *): Block write test passed for first 1KB
dma-two-blocks-driver reserved-driver@0:
Testing 50MB Block access...
(NULL device *): Memory block '50MB Block' test:
Wrote value: 0x12345678 at offset 0x100
Read value: 0x12345678
(NULL device *): Block write test passed for first 1KB
/ # cat /proc/meminfo
MemTotal: 511736 kB
MemFree: 497376 kB
MemAvailable: 496420 kB
Buffers: 232 kB
Cached: 1352 kB
SwapCached: 0 kB
Active: 2012 kB
Inactive: 128 kB
Active(anon): 576 kB
Inactive(anon): 12 kB
Active(file): 1436 kB
Inactive(file): 116 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 0 kB
Writeback: 0 kB
AnonPages: 556 kB
Mapped: 984 kB
Shmem: 32 kB
Slab: 7632 kB
SReclaimable: 4200 kB
SUnreclaim: 3432 kB
KernelStack: 416 kB
PageTables: 60 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 255868 kB
Committed_AS: 1312 kB
VmallocTotal: 1556480 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
CmaTotal: 102400 kB //可以看到cma的总大小为100M
CmaFree: 100608 kB
/ # free
total used free shared buff/cache available
Mem: 511736 8484 497464 32 5788 496512
Swap: 0 0 0

/ # cat /proc/iomem
60000000-7fffffff : System RAM
60008000-607039ff : Kernel code
6074e000-607a2037 : Kernel data

/ # cat /sys/kernel/debug/memblock/reserved
0: 0x60004000..0x60007fff
1: 0x60008280..0x607a2037
2: 0x68000000..0x6800ccbd
3: 0x70000000..0x763fffff
4: 0x7fb20000..0x7fbb7fff
5: 0x7fbb8440..0x7fbba43f
6: 0x7fbba474..0x7fffefff
7: 0x7ffffc00..0x7ffffc3b
8: 0x7ffffc40..0x7ffffc7b
9: 0x7ffffc80..0x7ffffcf7
10: 0x7ffffd00..0x7ffffd0f
11: 0x7ffffd40..0x7ffffd4f
12: 0x7ffffd80..0x7ffffd83
13: 0x7ffffdc0..0x7ffffdf1
14: 0x7ffffe00..0x7ffffe31
15: 0x7ffffe40..0x7ffffe71
16: 0x7ffffe80..0x7ffffe83
17: 0x7ffffea0..0x7ffffeea
18: 0x7ffffeec..0x7fffff06
19: 0x7fffff08..0x7fffff22
20: 0x7fffff24..0x7fffff3e
21: 0x7fffff40..0x7fffff9b
22: 0x7fffffb0..0x7fffffff

什么叫做常规内存分配分不到保留的区域?




kernel code 段:存放内核的指令,只读,不会用来放栈或动态数据;
kernel data 段:存放内核的全局 / 静态变量,编译时确定大小;
内核栈、task_struct、动态分配的内核对象:都是在运行时从物理内存的常规区通过伙伴系统 + slab 等分配器获取的。
2.专用型CMA
设备树:
/*
标记no-mao属性后,它的作用是如下:
1.内核不会为该区域创建页表映射,因此内核无法通过常规虚拟地址访问该物理内存;即使通过 phys_to_virt() 计算虚拟地址,也会因缺少页表映射而导致访问失败(触发页错误 Oops)
2.该区域会被标记为 MEMBLOCK_NOMAP,从内核的 “可访问内存池” 中彻底排除;
没有标记linux,cma-default会有以下影响:
则该区域不会被默认分配给通用型CMA,只有标记了linux,cma-default;属性,
才会被默认分配给通用型CMA.
*/
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
reserved: cma@70000000 {
compatible = "shared-dma-pool";
no-map; //相比通用型cma,使用no-map属性替代reusable
reg = <0x70000000 0x6400000>;
alignment = <0x2000>;
};
};
reserved-driver@0 {
compatible = "linux,virtual-free";
memory-region = <&reserved>;
};
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of_reserved_mem.h>
// 定义内存大小 (10MB 和 20MB)
#define MEM_SIZE_10M (10 * 1024 * 1024) // 10MB
#define MEM_SIZE_20M (20 * 1024 * 1024) // 20MB
// 内存块结构体
struct dma_memory_block {
void *virt_addr; // 内核虚拟地址
dma_addr_t dma_addr; // DMA总线地址
size_t size; // 内存大小
char *name; // 内存块名称
};
// 设备私有数据
struct dma_dev_priv {
struct dma_memory_block block1; // 10MB内存块
struct dma_memory_block block2; // 20MB内存块
};
// 测试数据写入和读取函数
static void test_memory_access(struct dma_memory_block *block)
{
u32 test_value = 0x12345678;
u32 read_back;
size_t test_offset = 0x100; // 测试偏移位置
// 简单测试:写入一个32位值
*(u32 *)(block->virt_addr + test_offset) = test_value;
read_back = *(u32 *)(block->virt_addr + test_offset);
dev_info(block->name ? NULL : NULL,
"Memory block '%s' test:
"
" Wrote value: 0x%x at offset 0x%zx
"
" Read value: 0x%x
",
block->name, test_value, test_offset, read_back);
// 块测试:写入一段数据
memset(block->virt_addr, 0xAA, 1024); // 写入前1KB
if (*(u8 *)block->virt_addr == 0xAA &&
*(u8 *)(block->virt_addr + 1023) == 0xAA) {
dev_info(block->name ? NULL : NULL,
" Block write test passed for first 1KB
");
} else {
dev_err(block->name ? NULL : NULL,
" Block write test failed!
");
}
}
static int dma_mem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dma_dev_priv *priv;
int ret = 0;
// 分配设备私有数据
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
// 初始化内存块信息
priv->block1.size = MEM_SIZE_10M;
priv->block1.name = "10MB Block";
priv->block2.size = MEM_SIZE_20M;
priv->block2.name = "20MB Block";
// 1. 绑定预留内存到设备(关键步骤),如果不调用则会使用默认的CMA区域
ret = of_reserved_mem_device_init(dev);
if (ret) {
dev_err(dev, "of_reserved_mem_device_init failed: %d
", ret);
return ret;
}
// 分配第一块内存 (10MB)
priv->block1.virt_addr = dma_alloc_coherent(dev,
priv->block1.size,
&priv->block1.dma_addr,
GFP_KERNEL);
if (!priv->block1.virt_addr) {
dev_err(dev, "Failed to allocate 10MB DMA memory
");
return -ENOMEM;
}
// 分配第二块内存 (20MB)
priv->block2.virt_addr = dma_alloc_coherent(dev,
priv->block2.size,
&priv->block2.dma_addr,
GFP_KERNEL);
if (!priv->block2.virt_addr) {
dev_err(dev, "Failed to allocate 20MB DMA memory
");
ret = -ENOMEM;
goto free_block1;
}
// 打印第一块内存信息
dev_info(dev, "
Allocated %s:
", priv->block1.name);
dev_info(dev, " Size: %zu bytes (%zu MB)
",
priv->block1.size, priv->block1.size / (1024 * 1024));
dev_info(dev, " Kernel virtual address: %p
", priv->block1.virt_addr);
dev_info(dev, " DMA bus address: %pad
", &priv->block1.dma_addr);
// 打印第二块内存信息
dev_info(dev, "
Allocated %s:
", priv->block2.name);
dev_info(dev, " Size: %zu bytes (%zu MB)
",
priv->block2.size, priv->block2.size / (1024 * 1024));
dev_info(dev, " Kernel virtual address: %p
", priv->block2.virt_addr);
dev_info(dev, " DMA bus address: %pad
", &priv->block2.dma_addr);
// 测试第一块内存的读写操作
dev_info(dev, "
Testing %s access...
", priv->block1.name);
test_memory_access(&priv->block1);
// 测试第二块内存的读写操作
dev_info(dev, "
Testing %s access...
", priv->block2.name);
test_memory_access(&priv->block2);
platform_set_drvdata(pdev, priv);
return 0;
free_block1:
dma_free_coherent(dev, priv->block1.size,
priv->block1.virt_addr, priv->block1.dma_addr);
return ret;
}
static int dma_mem_remove(struct platform_device *pdev)
{
struct dma_dev_priv *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
// 释放第二块内存
if (priv->block2.virt_addr) {
dma_free_coherent(dev, priv->block2.size,
priv->block2.virt_addr, priv->block2.dma_addr);
dev_info(dev, "Freed %s
", priv->block2.name);
}
// 释放第一块内存
if (priv->block1.virt_addr) {
dma_free_coherent(dev, priv->block1.size,
priv->block1.virt_addr, priv->block1.dma_addr);
dev_info(dev, "Freed %s
", priv->block1.name);
}
return 0;
}
static const struct of_device_id dma_mem_of_match[] = {
{ .compatible = "linux,virtual-free" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dma_mem_of_match);
static struct platform_driver dma_mem_driver = {
.probe = dma_mem_probe,
.remove = dma_mem_remove,
.driver = {
.name = "dma-two-blocks-driver",
.of_match_table = dma_mem_of_match,
},
};
module_platform_driver(dma_mem_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA Driver allocating two memory blocks (10MB and 20MB)");
MODULE_AUTHOR("Your Name");
内核输出信息: 可以内核分配了2个保留区域
1.设备树中配置的区域
Reserved memory: created DMA memory pool at 0x70000000, size 100 MiB
Reserved memory: initialized node cma@70000000, compatible id shared-dma-pool
2. .config文件中配置了48M的默认CMA区域
cma: Reserved 48 MiB at 0x7d000000
//可以看到当使用no-mao属性时,整体物理内存区域减小了100M,同时都没有被统计到整个区域中
Memory: 360792K/421888K available (5461K kernel code, 170K rwdata, 1688K rodata, 296K init, 156K bss, 11944K reserved, 49152K cma-reserved)
驱动输出信息如下: 如果不调用of_reserved_mem_device_init函数,可以看到分配的内存地址使用的默认CMA区域
dma-two-blocks-driver reserved-driver@0:
Allocated 10MB Block:
dma-two-blocks-driver reserved-driver@0: Size: 10485760 bytes (10 MB)
dma-two-blocks-driver reserved-driver@0: Kernel virtual address: 9d300000
dma-two-blocks-driver reserved-driver@0: DMA bus address: 0x7d300000
dma-two-blocks-driver reserved-driver@0:
Allocated 20MB Block:
dma-two-blocks-driver reserved-driver@0: Size: 20971520 bytes (20 MB)
dma-two-blocks-driver reserved-driver@0: Kernel virtual address: 9dd00000
dma-two-blocks-driver reserved-driver@0: DMA bus address: 0x7dd00000
dma-two-blocks-driver reserved-driver@0:
Testing 10MB Block access...
(NULL device *): Memory block '10MB Block' test:
Wrote value: 0x12345678 at offset 0x100
Read value: 0x12345678
(NULL device *): Block write test passed for first 1KB
dma-two-blocks-driver reserved-driver@0:
Testing 20MB Block access...
(NULL device *): Memory block '20MB Block' test:
Wrote value: 0x12345678 at offset 0x100
Read value: 0x12345678
(NULL device *): Block write test passed for first 1KB
驱动输出信息如下: 如果调用of_reserved_mem_device_init函数,可以看到分配的内存地址使用的是设备树中配置的区域
dma-two-blocks-driver reserved-driver@0: assigned reserved memory node cma@70000000
dma-two-blocks-driver reserved-driver@0:
Allocated 10MB Block:
dma-two-blocks-driver reserved-driver@0: Size: 10485760 bytes (10 MB)
dma-two-blocks-driver reserved-driver@0: Kernel virtual address: ab300000
dma-two-blocks-driver reserved-driver@0: DMA bus address: 0x70000000
dma-two-blocks-driver reserved-driver@0:
Allocated 20MB Block:
dma-two-blocks-driver reserved-driver@0: Size: 20971520 bytes (20 MB)
dma-two-blocks-driver reserved-driver@0: Kernel virtual address: ad300000
dma-two-blocks-driver reserved-driver@0: DMA bus address: 0x72000000
dma-two-blocks-driver reserved-driver@0:
Testing 10MB Block access...
(NULL device *): Memory block '10MB Block' test:
Wrote value: 0x12345678 at offset 0x100
Read value: 0x12345678
(NULL device *): Block write test passed for first 1KB
dma-two-blocks-driver reserved-driver@0:
Testing 20MB Block access...
(NULL device *): Memory block '20MB Block' test:
Wrote value: 0x12345678 at offset 0x100
Read value: 0x12345678
(NULL device *): Block write test passed for first 1KB
//可以看到配置了no-mao属性,整体内存变小
/ # free
total used free shared buff/cache available
Mem: 410240 8448 396036 32 5756 395300
Swap: 0 0 0
//no-map属性指示内核不要为该区域创建线性映射,该区域被标记为MEMBLOCK_NOMAP,内核不会将其视为常规的可预留内存区域进行展示
cat /sys/kernel/debug/memblock/reserved
0: 0x60004000..0x60007fff
1: 0x60008280..0x607a2037
2: 0x68000000..0x6800cc7d
3: 0x7cb3a000..0x7cbd1fff
4: 0x7cbd2480..0x7cbd447f
5: 0x7cbd44a8..0x7cde1fff
6: 0x7ceaa000..0x7cffefff
7: 0x7cfffbc0..0x7cfffbfb
8: 0x7cfffc00..0x7cfffc3b
9: 0x7cfffc40..0x7cfffcb7
10: 0x7cfffcc0..0x7cfffccf
11: 0x7cfffd00..0x7cfffd0f
12: 0x7cfffd40..0x7cfffd43
13: 0x7cfffd80..0x7cfffd83
14: 0x7cfffdc0..0x7cfffdf1
15: 0x7cfffe00..0x7cfffe31
16: 0x7cfffe40..0x7cfffe71
17: 0x7cfffe7c..0x7cfffec6
18: 0x7cfffec8..0x7cfffee2
19: 0x7cfffee4..0x7cfffefe
20: 0x7cffff00..0x7cffff1b
21: 0x7cffff24..0x7cffff3e
22: 0x7cffff40..0x7cffff9b
23: 0x7cffffb0..0x7fffffff
3.早期使用的预留内存
设备树定义:
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
reserved: cma@70000000 {
no-map;
reg = <0x70000000 0x6400000>;
alignment = <0x2000>;
};
};
reserved-driver@0 {
compatible = "linux,virtual-free";
memory-region = <&reserved>;
};
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/of_reserved_mem.h>
#include <linux/slab.h>
/* 设备私有数据结构 */
struct reserved_dev_data {
void __iomem *vaddr; // 虚拟地址(devm_memremap返回)
phys_addr_t paddr; // 物理地址
size_t size; // 内存大小
struct device *dev; // 设备指针
};
/* 探测函数:设备匹配时调用 */
static int reserved_mem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct reserved_dev_data *dev_data;
struct device_node *rmem_node;
struct resource res;
u32 read_val;
int ret;
u32 test_val = 0x12345678;
/* 1. 分配并初始化设备私有数据 */
dev_data = devm_kzalloc(dev, sizeof(*dev_data), GFP_KERNEL);
if (!dev_data) {
dev_err(dev, "Failed to allocate device data
");
return -ENOMEM;
}
dev_data->dev = dev;
/* 2. 从设备树获取内存区域节点 */
rmem_node = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!rmem_node) {
dev_err(dev, "Device tree has no 'memory-region' property
");
return -ENODEV;
}
/* 3. 解析物理地址和大小 */
ret = of_address_to_resource(rmem_node, 0, &res);
of_node_put(rmem_node); // 释放节点引用
if (ret) {
dev_err(dev, "Failed to parse memory resource: %d
", ret);
return ret;
}
dev_data->paddr = res.start;
dev_data->size = resource_size(&res);
dev_info(dev, "Found reserved memory:
");
dev_info(dev, " Physical address: 0x%pa
", &dev_data->paddr);
dev_info(dev, " Size: %zu bytes (%zu MB)
",
dev_data->size, dev_data->size / (1024 * 1024));
/* 4. 使用devm_memremap映射内存(自动管理生命周期) */
dev_data->vaddr = devm_memremap(dev, dev_data->paddr,
dev_data->size, MEMREMAP_WB);
if (IS_ERR(dev_data->vaddr)) {
dev_err(dev, "devm_memremap failed: %d
", ret);
return ret;
}
/* 5. 内存读写测试 */
*(u32 *)(dev_data->vaddr) = test_val;
read_val = *(u32 *)(dev_data->vaddr);
if (read_val == test_val) {
dev_info(dev, "Memory test passed: 0x%x == 0x%x
", test_val, read_val);
} else {
dev_err(dev, "Memory test failed: 0x%x != 0x%x
", test_val, read_val);
return -EIO;
}
/* 6. 保存私有数据 */
platform_set_drvdata(pdev, dev_data);
dev_info(dev, "Probe successful
");
return 0;
}
/* 移除函数:设备卸载时调用 */
static int reserved_mem_remove(struct platform_device *pdev)
{
struct reserved_dev_data *dev_data = platform_get_drvdata(pdev);
dev_info(dev_data->dev, "Device removed (devm will auto-release memory)
");
return 0;
}
/* 设备树匹配表 */
static const struct of_device_id reserved_mem_of_match[] = {
{ .compatible = "linux,virtual-free" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, reserved_mem_of_match);
/* 平台驱动结构体 */
static struct platform_driver reserved_mem_driver = {
.probe = reserved_mem_probe,
.remove = reserved_mem_remove,
.driver = {
.name = "reserved-mem-driver",
.of_match_table = reserved_mem_of_match,
},
};
module_platform_driver(reserved_mem_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Reserved Memory Driver with devm_memremap (supports reusable)");
MODULE_AUTHOR("Your Name");
内核输出信息: 只有.config中定义的cma区域,而没有dts中定义的区域
cma: Reserved 48 MiB at 0x7d000000
//同样定义了no-map区域,但是没有被统计到内存中
Memory: 360792K/421888K available (5461K kernel code, 170K rwdata, 1688K rodata, 296K init, 156K bss, 11944K reserved, 49152K cma-reserved)
//可以看到整体内存变小了
/ # free
total used free shared buff/cache available
Mem: 410240 8760 395732 32 5748 394992
Swap: 0 0 0
//no-map属性指示内核不要为该区域创建线性映射,该区域被标记为MEMBLOCK_NOMAP,内核不会将其视为常规的可预留内存区域进行展示
cat /sys/kernel/debug/memblock/reserved
0: 0x60004000..0x60007fff
1: 0x60008280..0x607a2037
2: 0x68000000..0x6800cc45
3: 0x7cb3a000..0x7cbd1fff
4: 0x7cbd24c0..0x7cbd44bf
5: 0x7cbd44dc..0x7cde1fff
6: 0x7ceaa000..0x7cffefff
7: 0x7cfffbc0..0x7cfffbfb
8: 0x7cfffc00..0x7cfffc3b
9: 0x7cfffc40..0x7cfffcb7
10: 0x7cfffcc0..0x7cfffccf
11: 0x7cfffd00..0x7cfffd0f
12: 0x7cfffd40..0x7cfffd43
13: 0x7cfffd80..0x7cfffd83
14: 0x7cfffdc0..0x7cfffdf1
15: 0x7cfffe00..0x7cfffe31
16: 0x7cfffe40..0x7cfffe71
17: 0x7cfffe7c..0x7cfffec6
18: 0x7cfffec8..0x7cfffee2
19: 0x7cfffee4..0x7cfffefe
20: 0x7cffff00..0x7cffff1b
21: 0x7cffff24..0x7cffff3e
22: 0x7cffff40..0x7cffff9b
23: 0x7cffffb0..0x7fffffff
驱动输出信息:
reserved-mem-driver reserved-driver@0: Found reserved memory:
reserved-mem-driver reserved-driver@0: Physical address: 0x0x70000000
reserved-mem-driver reserved-driver@0: Size: 104857600 bytes (100 MB)
reserved-mem-driver reserved-driver@0: Memory test passed: 0x12345678 == 0x12345678
reserved-mem-driver reserved-driver@0: Probe successful
- 普通预留内存 //相比早期预留内存,缺少了no-map属性
设备树信息如下:
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
reserved: cma@70000000 {
reg = <0x70000000 0x6400000>;
alignment = <0x2000>;
};
};
reserved-driver@0 {
compatible = "linux,virtual-free";
memory-region = <&reserved>;
};
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/of_reserved_mem.h>
#include <linux/memblock.h>
/* 设备私有数据结构 */
struct reserved_dev {
void __iomem *vaddr; /* 映射后的虚拟地址 */
phys_addr_t paddr; /* 物理地址 */
size_t size; /* 内存大小 */
};
/* 设备探测函数 */
static int reserved_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct reserved_dev *dev_data;
struct device_node *rmem_node;
struct resource res;
int ret;
u32 test_val, read_val;
size_t i;
/* 分配设备私有数据 */
dev_data = devm_kzalloc(dev, sizeof(*dev_data), GFP_KERNEL);
if (!dev_data)
return -ENOMEM;
/* 解析设备树中的内存区域 */
rmem_node = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!rmem_node) {
dev_err(dev, "Failed to get memory-region from device tree
");
return -ENODEV;
}
/* 获取物理地址和大小 */
ret = of_address_to_resource(rmem_node, 0, &res);
of_node_put(rmem_node);
if (ret) {
dev_err(dev, "Failed to parse memory resource: %d
", ret);
return ret;
}
dev_data->paddr = res.start;
dev_data->size = resource_size(&res);
/* 打印物理地址信息 */
dev_info(dev, "=== 物理地址信息 ===");
dev_info(dev, "起始物理地址: 0x%pa", &dev_data->paddr);
dev_info(dev, "结束物理地址: 0x%pa", &(phys_addr_t){dev_data->paddr + dev_data->size - 1});
dev_info(dev, "内存大小: %zu 字节", dev_data->size);
// dev_info(dev, "memremap前通过phys_to_virt得到的虚拟地址: 0x%p", phys_to_virt(dev_data->paddr));
/* 映射内存 */
dev_data->vaddr = memremap(dev_data->paddr, dev_data->size, MEMREMAP_WB);
if (!dev_data->vaddr) {
dev_err(dev, "Failed to memremap reserved memory
");
ret = -ENOMEM;
goto release_mem;
}
/* 打印虚拟地址信息 */
dev_info(dev, "=== 虚拟地址信息 ===");
dev_info(dev, "起始虚拟地址: 0x%p", dev_data->vaddr);
dev_info(dev, "结束虚拟地址: 0x%p", dev_data->vaddr + dev_data->size - 1);
/* 写入测试数据 */
test_val = 0xAA55AA55;
dev_info(dev, "开始写入测试数据 (起始值: 0x%x)", test_val);
/* 填充整个内存区域 */
for (i = 0; i < dev_data->size; i += sizeof(u32)) {
iowrite32(test_val, dev_data->vaddr + i);
test_val += 0x11111111;
}
/* 读取验证 */
test_val = 0xAA55AA55;
dev_info(dev, "开始验证内存数据...");
for (i = 0; i < dev_data->size; i += sizeof(u32)) {
read_val = ioread32(dev_data->vaddr + i);
if (read_val != test_val) {
dev_err(dev, "地址偏移 0x%zu 验证失败: 预期 0x%x, 实际 0x%x",
i, test_val, read_val);
goto unmap_mem;
}
test_val += 0x11111111;
}
dev_info(dev, "内存读写测试成功完成");
platform_set_drvdata(pdev, dev_data);
free_reserved_area( dev_data->vaddr, dev_data->vaddr+dev_data->size,-1,NULL);
return 0;
unmap_mem:
memunmap(dev_data->vaddr);
release_mem:
of_reserved_mem_device_release(dev);
return ret;
}
/* 设备移除函数 */
static int reserved_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct reserved_dev *dev_data = platform_get_drvdata(pdev);
/* 手动释放内存映射 */
if (dev_data && dev_data->vaddr) {
dev_info(dev, "释放虚拟地址映射: 0x%p", dev_data->vaddr);
memunmap(dev_data->vaddr);
dev_data->vaddr = NULL;
}
/* 释放预留内存绑定 */
of_reserved_mem_device_release(dev);
dev_info(dev, "预留内存已释放 (物理地址: 0x%pa)", &dev_data->paddr);
return 0;
}
/* 设备树匹配表 */
static const struct of_device_id reserved_of_match[] = {
{ .compatible = "linux,virtual-free" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, reserved_of_match);
/* 平台驱动结构体 */
static struct platform_driver reserved_driver = {
.probe = reserved_probe,
.remove = reserved_remove,
.driver = {
.name = "reserved-memory-driver",
.of_match_table = reserved_of_match,
},
};
module_platform_driver(reserved_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Reserved Memory Driver with Address Printing");
MODULE_AUTHOR("Your Name");
内核输出信息如下:
//可以看到只有默认的cma区域
cma: Reserved 48 MiB at 0x7d000000
//可以看到整体内存并没有减少,但是可用区域是减少到,并且100M的预留内存存放到了reserved区域中
Memory: 359992K/524288K available (5461K kernel code, 170K rwdata, 1688K rodata, 296K init, 156K bss, 115144K reserved, 49152K cma-reserved)
加载驱动前,可以看到整体total内存是409440k
/ # free
total used free shared buff/cache available
Mem: 409440 7884 395792 32 5764 395068
Swap: 0 0 0
加载驱动后,可以看到整体total内存是511840,整体可以内存提高了,由于在驱动中释放了内存
free
total used free shared buff/cache available
Mem: 511840 7916 497916 32 6008 497304
Swap: 0 0 0
//可以看到这里也能看到预留的内存
/ # cat /sys/kernel/debug/memblock/reserved
0: 0x60004000..0x60007fff
1: 0x60008280..0x607a2037
2: 0x68000000..0x6800cc1f
3: 0x70000000..0x763fffff // 100M的预留内存
4: 0x7cb3a000..0x7cbd1fff
5: 0x7cbd2500..0x7cbd44ff
6: 0x7cbd4510..0x7cffefff
7: 0x7cfffc00..0x7cfffc3b
8: 0x7cfffc40..0x7cfffc7b
9: 0x7cfffc80..0x7cfffcf7
10: 0x7cfffd00..0x7cfffd0f
11: 0x7cfffd40..0x7cfffd4f
12: 0x7cfffd80..0x7cfffd83
13: 0x7cfffdc0..0x7cfffdf1
14: 0x7cfffe00..0x7cfffe31
15: 0x7cfffe40..0x7cfffe71
16: 0x7cfffe80..0x7cfffe83
17: 0x7cfffea0..0x7cfffeea
18: 0x7cfffeec..0x7cffff06
19: 0x7cffff08..0x7cffff22
20: 0x7cffff24..0x7cffff3e
21: 0x7cffff40..0x7cffff9b
22: 0x7cffffb0..0x7fffffff
reserved-memory-driver reserved-driver@0: === 物理地址信息 ===
reserved-memory-driver reserved-driver@0: 起始物理地址: 0x0x70000000
reserved-memory-driver reserved-driver@0: 结束物理地址: 0x0x763fffff
reserved-memory-driver reserved-driver@0: 内存大小: 104857600 字节
reserved-memory-driver reserved-driver@0: === 虚拟地址信息 ===
reserved-memory-driver reserved-driver@0: 起始虚拟地址: 0x90000000
reserved-memory-driver reserved-driver@0: 结束虚拟地址: 0x963fffff
reserved-memory-driver reserved-driver@0: 开始写入测试数据 (起始值: 0xaa55aa55)
reserved-memory-driver reserved-driver@0: 开始验证内存数据...
reserved-memory-driver reserved-driver@0: 内存读写测试成功完成
5./memreserve/节点的使用 这种方式简单,如果没有回收的要求,可以使用这种方式
设备树信息:
/memreserve/ 0x70000000 0x6400000;
可以看到物理整体大小没有变化,但是可用内存减少了,由于预留的内存被放到了reserved区域中
Memory: 359992K/524288K available (5461K kernel code, 170K rwdata, 1688K rodata, 296K init, 156K bss, 115144K reserved, 49152K cma-reserved)
//可以看到可用内存减少了
/ # free
total used free shared buff/cache available
Mem: 409440 8060 395628 32 5752 394896
Swap: 0 0 0
//可以看到预留的内存被放到了reserved区域中
/ # cat /sys/kernel/debug/memblock/reserved
0: 0x60004000..0x60007fff
1: 0x60008280..0x607a2037
2: 0x68000000..0x6800ca53
3: 0x70000000..0x763fffff
4: 0x7cb3a000..0x7cbd1fff
5: 0x7cbd28c0..0x7cbd48bf
6: 0x7cbd48f4..0x7cffefff
7: 0x7cfffc00..0x7cfffc3b
8: 0x7cfffc40..0x7cfffc7b
9: 0x7cfffc80..0x7cfffcf7
10: 0x7cfffd00..0x7cfffd0f
11: 0x7cfffd40..0x7cfffd4f
12: 0x7cfffd80..0x7cfffd83
13: 0x7cfffdc0..0x7cfffdf1
14: 0x7cfffe00..0x7cfffe31
15: 0x7cfffe40..0x7cfffe71
16: 0x7cfffe80..0x7cfffe83
17: 0x7cfffea0..0x7cfffeea
18: 0x7cfffeec..0x7cffff06
19: 0x7cffff08..0x7cffff22
20: 0x7cffff24..0x7cffff3e
21: 0x7cffff40..0x7cffff9b
22: 0x7cffffb0..0x7fffffff
6.将一块内存分为多个物理段
//mem格式是size@addr
uboot中bootargs设置为:console=ttyAMA0,115200 rootwait root=/dev/mmcblk0 mem=256M@0x60000000 mem=255M@0x70100000
内核信息如下:
Kernel command line: console=ttyAMA0,115200 rootwait root=/dev/mmcblk0 mem=256M@0x60000000 mem=255M@0x70100000
可以看到整体物理内存减小了1M
Memory: 461368K/523264K available (5461K kernel code, 170K rwdata, 1688K rodata, 296K init, 156K bss, 12744K reserved, 49152K cma-reserved)
//输出表明系统存在两个独立的可用物理内存段,可以看到输出了2段物理内存区域
/ # cat /sys/kernel/debug/memblock/memory
0: 0x60000000..0x6fffffff
1: 0x70100000..0x7fffffff
/ # cat /sys/kernel/debug/memblock/reserved
0: 0x60004000..0x60007fff
1: 0x60008280..0x607a2037
2: 0x68000000..0x6800ca33
3: 0x7cb3a000..0x7cbd1fff
4: 0x7cbd28c0..0x7cbd48bf
5: 0x7cbd48f4..0x7cffefff
6: 0x7cfffb00..0x7cfffb3b
7: 0x7cfffb40..0x7cfffb7b
8: 0x7cfffb80..0x7cfffbf7
9: 0x7cfffc00..0x7cfffc0f
10: 0x7cfffc40..0x7cfffc4f
11: 0x7cfffc80..0x7cfffc83
12: 0x7cfffcc0..0x7cfffcc3
13: 0x7cfffd00..0x7cfffd59
14: 0x7cfffd80..0x7cfffdd9
15: 0x7cfffe00..0x7cfffe59
16: 0x7cfffe7c..0x7cfffec6
17: 0x7cfffec8..0x7cfffee2
18: 0x7cfffee4..0x7cfffefe
19: 0x7cffff00..0x7cffff1b
20: 0x7cffff24..0x7cffff3e
21: 0x7cffff40..0x7cffff9b
22: 0x7cffffb0..0x7fffffff
总结:
1.通过内核日志先看整体物理内存有多少,然后再通过free命令看可用内存有多少
2.通过cat /sys/kernel/debug/memblock/reserved 命令看预留的内存的位置
图片
✅ 结论
输出结论
待查资料问题
- ❓ 问题 1:?
- ❓ 问题 2:?
参考链接
- 官方文档
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END
















暂无评论内容