設置Linux保留物理內存arm_memblock_init並使用 (1)

本文介绍了在Linux系统中通过memblock设置系统保留物理内存的方法,包括三种不同的实现方式:通过修改内核源码预留内存、预留并释放内存创建空洞以及通过uboot参数指定可用内存范围。

在Linux系統中可以通過memblock來設置系統保留物理內存,防止這些內存被內存管理系統分配出去。

 

作者: 彭東林

郵箱: pengdonglin137@163.com

 

平臺

硬件平臺: TQ2440

Linux版本:Linux 3.14.45

 

說明

1. 在tq2440上,物理內存的起始地址是0x30000000,一共有64MB的內存,所以物理內存地址範圍是: 0x30000000 -> 0x33ffffff

2. 可以在uboot傳給kernel的參數bootargs中添加一個"memblock=debug",這樣在Linux啓動的時候,會將設置memblock的信息打印出來

 

參考博文

Linux内核---41.arm 内存初始化

http://bbs.chinaunix.net/thread-4143403-1-1.html

 

代碼調用

在Linux啓動的時候會調用machine相關的代碼定製部分系統保留內存,函數調用如下:

start_kernel

    ----> setup_arch

            ----> arm_memblock_init

                     ----> mdesc->reserve()

所以我們可以修改machine相關的代碼,添加reserve函數的實現。

 

方法一

修改mach-tq2440.c如下:

复制代码
 1 diff --git a/arch/arm/mach-s3c24xx/mach-tq2440.c b/arch/arm/mach-s3c24xx/mach-tq2440.c
 2 index f9679fb..da75db2 100644
 3 --- a/arch/arm/mach-s3c24xx/mach-tq2440.c
 4 +++ b/arch/arm/mach-s3c24xx/mach-tq2440.c
 5 @@ -23,6 +23,7 @@
 6  #include <linux/platform_device.h>
 7  #include <linux/io.h>
 8  #include <linux/dm9000.h>
 9 +#include <linux/memblock.h>
10  
11  #include <asm/mach/arch.h>
12  #include <asm/mach/map.h>
13 @@ -503,11 +504,28 @@ static void __init tq2440_machine_init(void)
14         s3c_pm_init();
15  }
16  
17 +static void __init tq2440_reserve(void) {
18 +    u32 paddr,size;
19 +
20 +    printk("%s enter.\n", __func__);
21 +
22 +    paddr = 0x32000000;
23 +    size = 0x200000;
24 +
25 +    if (memblock_reserve(paddr, size) < 0) {
26 +        pr_err("failed to reserve DRAM - no memory\n");
27 +               return;
28 +    }
29 +
30 +    printk("reserve : reserve %dM mem\n", size>>20);
31 +}
32 +
33  MACHINE_START(TQ2440, "TQ2440")
34         /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
35         .atag_offset    = 0x100,
36  
37         .init_irq       = s3c2440_init_irq,
38 +    .reserve    = tq2440_reserve,
39         .map_io         = tq2440_map_io,
40         .init_machine   = tq2440_machine_init,
41         .init_time      = samsung_timer_init,
复制代码

 上面我們在0x32000000開始的地方保留了2MB的物理內存,然後調用memblock_reserve告訴系統。這個在系統的啓動信息中可以看到:

复制代码
 1 [    0.000000] Machine: TQ2440
 2 [    0.000000] memblock_reserve: [0x00000030008200-0x0000003057fc03] flags 0x0 arm_memblock_init+0x4c/0x1bc
 3 [    0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x158/0x1bc
 4 [    0.000000] tq2440_reserve enter.
 5 [    0.000000] memblock_reserve: [0x00000032000000-0x000000321fffff] flags 0x0 tq2440_reserve+0x1c/0x50
 6 [    0.000000] reserve : reserve 2M mem
 7 [    0.000000] MEMBLOCK configuration:
 8 [    0.000000]  memory size = 0x4000000 reserved size = 0x77ba04
 9 [    0.000000]  memory.cnt  = 0x1
10 [    0.000000]  memory[0x0]    [0x00000030000000-0x00000033ffffff], 0x4000000 bytes flags: 0x0
11 [    0.000000]  reserved.cnt  = 0x3
12 [    0.000000]  reserved[0x0]    [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
13 [    0.000000]  reserved[0x1]    [0x00000030008200-0x0000003057fc03], 0x577a04 bytes flags: 0x0
14 [    0.000000]  reserved[0x2]    [0x00000032000000-0x000000321fffff], 0x200000 bytes flags: 0x0
复制代码

  上面的第5行和第14行就是我們自己設置的保留內存範圍信息。在Linux啓動後,在debugfs中也可以看到memblock的信息:

复制代码
[root@TQ2440 /]# cat /sys/kernel/debug/memblock/memory 
   0: 0x30000000..0x33ffffff
[root@TQ2440 /]# cat /sys/kernel/debug/memblock/reserved 
   0: 0x30004000..0x30007fff
   1: 0x30008200..0x3057fc03
   2: 0x32000000..0x321fffff
   3: 0x33f60000..0x33ffbfff
   4: 0x33ffc540..0x33ffc96b
   5: 0x33ffc980..0x33ffc9f7
   6: 0x33ffca00..0x33ffca03
   7: 0x33ffca20..0x33ffca23
   8: 0x33ffca40..0x33ffca43
   9: 0x33ffca60..0x33ffca63
  10: 0x33ffca80..0x33ffcad2
  11: 0x33ffcae0..0x33ffcb32
  12: 0x33ffcb40..0x33ffcb92
  13: 0x33ffcba0..0x33ffcbbb
  14: 0x33ffcbc0..0x33ffcdc7
  15: 0x33ffcdd0..0x33ffffff
复制代码

  其中memory節點中存放的是可用的物理內存地址範圍,reserved表示已經分配出去的物理地址,第2行記錄的就是我們設置的。

既然添加了保留物理內存,那麼如何使用呢?下面我寫了一個簡單的內核模塊,使用我們添加的保留物理內存,下面是內核模塊的代碼:

复制代码
 1 #include <linux/module.h>
 2 
 3 #define RESERVE_PHY  0x32000000
 4 #define RESERVE_SIZE 0x200000
 5 
 6 static char str[] = "pengdonglin137@163.com\n";
 7 
 8 static __init int reserve_demo_init(void)
 9 {
10     memcpy(phys_to_virt(RESERVE_PHY), str, sizeof(str));
11 
12     printk("%s: virt: %p\n", __func__, phys_to_virt(RESERVE_PHY));
13 
14     return 0;
15 }
16 
17 static __exit void reserve_demo_exit(void)
18 {
19     printk("%s: %s\n", __func__, (char *)phys_to_virt(RESERVE_PHY));
20 }
21 
22 module_init(reserve_demo_init);
23 module_exit(reserve_demo_exit);
24 MODULE_LICENSE("GPL");
复制代码

  可以看到,我們直接調用函數phys_to_virt將物理地址轉換成虛擬地址,然後直接向這個虛擬地址中寫入數據,在模塊卸載時再將內容打印出出來。

复制代码
1 [root@TQ2440 /]# insmod nfs/demo.ko 
2 [ 1417.153362] reserve_demo_init: virt: c2000000
3 [root@TQ2440 /]# 
4 [root@TQ2440 /]# rmmod demo
5 [ 1420.986938] reserve_demo_exit: pengdonglin137@163.com
6 [ 1420.986938] 
复制代码

 可以看到,第2行中得到物理地址0x32000000對應的虛擬地址是0xC2000000。在模塊卸載時打印出了我們之前寫入的內容。

這種方法使用與物理內存在Normal區域的情況。

 

方法二

修改mach-tq2440.c

复制代码
 1 diff --git a/arch/arm/mach-s3c24xx/mach-tq2440.c b/arch/arm/mach-s3c24xx/mach-tq2440.c
 2 index f9679fb..345a868 100644
 3 --- a/arch/arm/mach-s3c24xx/mach-tq2440.c
 4 +++ b/arch/arm/mach-s3c24xx/mach-tq2440.c
 5 @@ -23,6 +23,7 @@
 6  #include <linux/platform_device.h>
 7  #include <linux/io.h>
 8  #include <linux/dm9000.h>
 9 +#include <linux/memblock.h>
10  
11  #include <asm/mach/arch.h>
12  #include <asm/mach/map.h>
13 @@ -503,11 +504,31 @@ static void __init tq2440_machine_init(void)
14         s3c_pm_init();
15  }
16  
17 +static void __init tq2440_reserve(void) {
18 +    u32 paddr,size;
19 +
20 +    printk("%s enter.\n", __func__);
21 +
22 +    paddr = 0x32000000;
23 +    size = 0x200000;
24 +
25 +    if (memblock_reserve(paddr, size) < 0) {
26 +        pr_err("failed to reserve DRAM - no memory\n");
27 +               return;
28 +    }
29 +
30 +    memblock_free(paddr, size);
31 +    memblock_remove(paddr, size);
32 +
33 +    printk("reserve : reserve %dM mem\n", size>>20);
34 +}
35 +
36  MACHINE_START(TQ2440, "TQ2440")
37         /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
38         .atag_offset    = 0x100,
39  
40         .init_irq       = s3c2440_init_irq,
41 +    .reserve    = tq2440_reserve,
42         .map_io         = tq2440_map_io,
43         .init_machine   = tq2440_machine_init,
44         .init_time      = samsung_timer_init,
复制代码

 用新kernel啓動,在啓動信息中可以看到:

复制代码
 1 [    0.000000] Machine: TQ2440
 2 [    0.000000] memblock_reserve: [0x00000030008200-0x0000003057fc03] flags 0x0 arm_memblock_init+0x4c/0x1bc
 3 [    0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x158/0x1bc
 4 [    0.000000] tq2440_reserve enter.
 5 [    0.000000] memblock_reserve: [0x00000032000000-0x000000321fffff] flags 0x0 tq2440_reserve+0x1c/0x68
 6 [    0.000000]    memblock_free: [0x00000032000000-0x000000321fffff] tq2440_reserve+0x3c/0x68
 7 [    0.000000] reserve : reserve 2M mem
 8 [    0.000000] MEMBLOCK configuration:
 9 [    0.000000]  memory size = 0x3e00000 reserved size = 0x57ba04
10 [    0.000000]  memory.cnt  = 0x2
11 [    0.000000]  memory[0x0]    [0x00000030000000-0x00000031ffffff], 0x2000000 bytes flags: 0x0
12 [    0.000000]  memory[0x1]    [0x00000032200000-0x00000033ffffff], 0x1e00000 bytes flags: 0x0
13 [    0.000000]  reserved.cnt  = 0x2
14 [    0.000000]  reserved[0x0]    [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
15 [    0.000000]  reserved[0x1]    [0x00000030008200-0x0000003057fc03], 0x577a04 bytes flags: 0x0
复制代码

 在kernel啓動後,在memory和reserved節點中:

[root@TQ2440 /]# cat /sys/kernel/debug/memblock/memory 
   0: 0x30000000..0x31ffffff
   1: 0x32200000..0x33ffffff

 可以看到,此時系統中有兩塊物理內存,但是這兩塊物理內存的地址之間不連續,中間有一個大小爲2MB的“洞”。此時在reserved節點看不到這部分內存。

复制代码
[root@TQ2440 /]# cat /sys/kernel/debug/memblock/reserved 
   0: 0x30004000..0x30007fff
   1: 0x30008200..0x3057fc03
   2: 0x33f60000..0x33ffbfff
   3: 0x33ffc520..0x33ffc94b
   4: 0x33ffc960..0x33ffc9d7
   5: 0x33ffc9e0..0x33ffc9e3
   6: 0x33ffca00..0x33ffca03
   7: 0x33ffca20..0x33ffca23
   8: 0x33ffca40..0x33ffca43
   9: 0x33ffca60..0x33ffcab2
  10: 0x33ffcac0..0x33ffcb12
  11: 0x33ffcb20..0x33ffcb72
  12: 0x33ffcb80..0x33ffcb9b
  13: 0x33ffcba0..0x33ffcbbb
  14: 0x33ffcbc0..0x33ffcdc7
  15: 0x33ffcdd0..0x33ffffff
复制代码

此時在使用這部分保留內存時就不能直接使用了,需要申請,然後映射後才能使用,如下:

复制代码
 1 #include <linux/module.h>
 2 #include <linux/ioport.h>
 3 #include <linux/io.h>
 4 
 5 #define RESERVE_PHY  0x32000000
 6 #define RESERVE_SIZE 0x200000
 7 
 8 static char str[] = "pengdonglin137@163.com\n";
 9 static void __iomem *addr;
10 
11 static __init int reserve_demo_init(void)
12 {
13 
14     if (!request_mem_region(RESERVE_PHY, RESERVE_SIZE, "my reserve"))
15         return  -EBUSY;
16 
17     addr = ioremap_nocache(RESERVE_PHY, RESERVE_SIZE);
18     memcpy(addr, str, sizeof(str));
19 
20     printk("%s: virt: %p\n", __func__, addr);
21 
22     return 0;
23 }
24 
25 static __exit void reserve_demo_exit(void)
26 {
27     printk("%s: %s\n", __func__, (char *)addr);
28 
29     iounmap(addr);
30     release_mem_region(RESERVE_PHY, RESERVE_SIZE);
31 }
32 
33 module_init(reserve_demo_init);
34 module_exit(reserve_demo_exit);
35 MODULE_LICENSE("GPL");
复制代码

 然後加載這個模塊,此時會向addr中寫入字符串,在卸載時再打印出來。log如下:

复制代码
1 [root@TQ2440 /]# insmod nfs/demo.ko 
2 [   43.853848] reserve_demo_init: virt: c5000000
3 [root@TQ2440 /]# 
4 [root@TQ2440 /]# rmmod demo
5 [   49.247204] reserve_demo_exit: pengdonglin137@163.com
6 [   49.247204] 
7 [root@TQ2440 /]# 
复制代码

 

方法三

在uboot可以通過在bootargs中設置mem參數,告訴kernel可用的物理內存。

修改uboot傳給kernel的bootargs,如下:

setenv bootargs "memblock=debug noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200n8 mem=32M"

 此時啓動Linux,啓動信息中可以看到:

复制代码
[    0.000000] memblock_reserve: [0x00000030008200-0x00000030589c83] flags 0x0 arm_memblock_init+0x5c/0x1d8
[    0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x8c/0x1d8
[    0.000000] MEMBLOCK configuration:
[    0.000000]  memory size = 0x2000000 reserved size = 0x585a84
[    0.000000]  memory.cnt  = 0x1
[    0.000000]  memory[0x0]    [0x00000030000000-0x00000031ffffff], 0x2000000 bytes flags: 0x0
[    0.000000]  reserved.cnt  = 0x2
[    0.000000]  reserved[0x0]    [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
[    0.000000]  reserved[0x1]    [0x00000030008200-0x00000030589c83], 0x581a84 bytes flags: 0x0
... ...
[    0.000000] Memory: 26752K/32768K available (3912K kernel code, 214K rwdata, 1176K rodata, 155K init, 176K bss, 6016K reserved)
复制代码

 可以看到,此時Linux只知道有32MB的物理內存,而實際上板子上有64MB的物理內存。

可以看到memory和reserved節點的內容如下:

复制代码
 1 [root@TQ2440 /]# cat /d/memblock/memory 
 2    0: 0x30000000..0x31ffffff
 3 [root@TQ2440 /]# 
 4 [root@TQ2440 /]# cat /d/memblock/reserved 
 5    0: 0x30004000..0x30007fff
 6    1: 0x30008200..0x30589c83
 7    2: 0x31fa6000..0x31ffbfff
 8    3: 0x31ffc840..0x31ffca6b
 9    4: 0x31ffca80..0x31ffcaf7
10    5: 0x31ffcb00..0x31ffcb03
11    6: 0x31ffcb20..0x31ffcb23
12    7: 0x31ffcb40..0x31ffcb43
13    8: 0x31ffcb60..0x31ffcb63
14    9: 0x31ffcb80..0x31ffcbda
15   10: 0x31ffcbe0..0x31ffcc3a
16   11: 0x31ffcc40..0x31ffcc9a
17   12: 0x31ffcca0..0x31ffccbb
18   13: 0x31ffccc0..0x31ffcdc3
19   14: 0x31ffcdd0..0x31ffffff
复制代码

 使用剩餘的物理內存,同樣需要先申請,再映射,然後再使用:

复制代码
 1 #include <linux/module.h>
 2 #include <linux/ioport.h>
 3 #include <linux/io.h>
 4 
 5 #define RESERVE_PHY  0x32000000
 6 #define RESERVE_SIZE 0x200000
 7 
 8 static char str[] = "pengdonglin137@163.com\n";
 9 static void __iomem *addr;
10 
11 static __init int reserve_demo_init(void)
12 {
13 
14     if (!request_mem_region(RESERVE_PHY, RESERVE_SIZE, "my reserve"))
15         return  -EBUSY;
16 
17     addr = ioremap_nocache(RESERVE_PHY, RESERVE_SIZE);
18     memcpy(addr, str, sizeof(str));
19 
20     printk("%s: virt: %p\n", __func__, addr);
21 
22     return 0;
23 }
24 
25 static __exit void reserve_demo_exit(void)
26 {
27     printk("%s: %s\n", __func__, (char *)addr);
28 
29     iounmap(addr);
30     release_mem_region(RESERVE_PHY, RESERVE_SIZE);
31 }
32 
33 module_init(reserve_demo_init);
34 module_exit(reserve_demo_exit);
35 MODULE_LICENSE("GPL");
复制代码

 加載模塊,然後卸載,可以看到log:

复制代码
1 root@TQ2440 /]# insmod /nfs/demo.ko 
2 [  266.998897] reserve_demo_init: virt: c3000000
3 [root@TQ2440 /]# 
4 [root@TQ2440 /]# rmmod demo
5 [  271.735731] reserve_demo_exit: pengdonglin137@163.com
6 [  271.735731] 
复制代码
<think> 嗯,用户想了解Linux系统中memblock的详细情况。根据引用内容,memblock似乎是内核早期启动阶段的内存管理机制。用户引用了三条资料:第一条提到memblock_free()很少使用,因为早期分配的内存多用于永久用途;第二条是关于memblock的debug功能,可以通过内核参数启用;第三条则指出可以通过sysfs查看memblock信息。 用户可能正在研究Linux内核内存管理,特别是启动阶段的机制。ta的专业程度应该不低,因为memblock属于比较底层的实现。考虑到引用内容相对零散,需要整合成系统化的说明。 memblock的核心功能是内存块管理,主要包括memory和reserved两个区域。在boot阶段,内存管理器尚未建立时,内核就靠它来记录可用物理内存和预留区域。等伙伴系统初始化后,memblock的任务就完成了。 用户可能还想了解具体使用场景,比如架构相关的初始化、设备树处理等。另外debug方法也很实用,特别是early_param和sysfs接口,这对内核开发者调试应该很有帮助。 需要注意术语的准确性,比如要区分“物理内存添加”和“预留区域管理”。内核源码里的memblock_add()memblock_reserve()是两大基础操作,这个必须讲清楚。还有内存类型physmem,这是用来兼容旧bootloader的细节。 考虑到用户引用了代码片段,说明ta可能接触过内核源码。可以适当提及代码位置(mm/memblock.c),但不必展开具体实现。生命周期部分很重要,要强调该机制只在启动阶段有效,避免用户误解它是持久化的内存管理方案。 </think> 以下是Linux内核中`memblock`子系统的详细说明,结合引用内容及内核实现原理整理: --- ### **memblock 概述** `memblock`是Linux内核在**启动早期**(伙伴分配器`buddy allocator`初始化前)使用的**物理内存管理器**。它通过两个核心数组管理内存1. `memory`:记录所有可用的物理内存区域 2. `reserved`:记录已被预留或分配的内存区域 其设计目标是为内核启动阶段提供简单可靠的内存分配能力[^1]。 --- ### **核心操作与工作流程** #### 1. **内存区域注册** - **`memblock_add(base, size)`** 将物理地址范围`[base, base+size)`加入`memory`数组,标记为可用内存 示例:`memblock_add(0x60000000, 0x20000000)` 添加512MB物理内存[^1] #### 2. **内存预留/分配** - **`memblock_reserve(base, size)`** 将指定范围从`memory`移入`reserved`数组(如内核代码区、设备树等) - **`memblock_alloc(size, align)`** 在`memory`中寻找可用区域,分配后将其加入`reserved` ► 分配后**极少调用`memblock_free()`**:因早期分配多为永久使用(如内核数据结构)[^1] #### 3. **生命周期** ```mermaid graph LR A[引导加载器传递内存映射] --> B[memblock初始化] B --> C[注册物理内存区域] C --> D[预留内核/initrd/设备树等] D --> E[伙伴分配器初始化] E --> F[memblock数据迁移至伙伴系统] F --> G[memblock停用] ``` --- ### **关键设计特点** 1. **简单链表结构** 物理内存被划分为若干`memblock_region`,按地址排序存储在`memory`/`reserved`数组中 ```c struct memblock_region { phys_addr_t base; phys_addr_t size; unsigned long flags; }; ``` 2. **无碎片化顾虑** 启动阶段仅需分配少量永久内存,无需复杂的内存回收机制[^1] 3. **与架构解耦** ARM/x86等均通过`memblock_add()`注册物理内存,统一抽象层 --- ### **调试与监控** 1. **内核启动参数** `memblock=debug`启用调试输出(需内核配置`CONFIG_DEBUG_FS=y`)[^2] 2. **查看运行时状态** ```bash cat /sys/kernel/debug/memblock/memory # 查看可用内存区域 cat /sys/kernel/debug/memblock/reserved # 查看预留内存区域 ``` 输出示例: ``` 0: 0x60000000..0x7fffffff # 512MB可用内存 1: 0x80000000..0x80007fff # 32KB预留区域(内核代码) ``` --- ### **典型应用场景** 1. 内核镜像加载位置的预留 2. 初始化页表`pgd`的内存分配 3. 设备树二进制`DTB`的拷贝与保留 4. 为早期的`slab`分配器提供内存池 > ⚠️ 注意:当伙伴分配器完成初始化后,`memblock`会将所有内存移交给伙伴系统,自身停止服务[^3]。 --- ### **与其他子系统关系** ```mermaid graph TD Bootloader -->|传递内存映射| memblock memblock -->|提供物理内存| setup_arch() memblock -->|预留内核区域| arm_memblock_init() memblock -->|迁移数据| mm_init() mm_init() -->|初始化完成| buddy_allocator ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值