在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如下:

 diff --git a/arch/arm/mach-s3c24xx/mach-tq2440.c b/arch/arm/mach-s3c24xx/mach-tq2440.c
index f9679fb..da75db2
--- a/arch/arm/mach-s3c24xx/mach-tq2440.c
+++ b/arch/arm/mach-s3c24xx/mach-tq2440.c
@@ -, +, @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/dm9000.h>
+#include <linux/memblock.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -, +, @@ static void __init tq2440_machine_init(void)
s3c_pm_init();
} +static void __init tq2440_reserve(void) {
+ u32 paddr,size;
+
+ printk("%s enter.\n", __func__);
+
+ paddr = 0x32000000;
+ size = 0x200000;
+
+ if (memblock_reserve(paddr, size) < ) {
+ pr_err("failed to reserve DRAM - no memory\n");
+ return;
+ }
+
+ printk("reserve : reserve %dM mem\n", size>>);
+}
+
MACHINE_START(TQ2440, "TQ2440")
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.atag_offset = 0x100, .init_irq = s3c2440_init_irq,
+ .reserve = tq2440_reserve,
.map_io = tq2440_map_io,
.init_machine = tq2440_machine_init,
.init_time = samsung_timer_init,

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

 [    0.000000] Machine: TQ2440
[ 0.000000] memblock_reserve: [0x00000030008200-0x0000003057fc03] flags 0x0 arm_memblock_init+0x4c/0x1bc
[ 0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x158/0x1bc
[ 0.000000] tq2440_reserve enter.
[ 0.000000] memblock_reserve: [0x00000032000000-0x000000321fffff] flags 0x0 tq2440_reserve+0x1c/0x50
[ 0.000000] reserve : reserve 2M mem
[ 0.000000] MEMBLOCK configuration:
[ 0.000000] memory size = 0x4000000 reserved size = 0x77ba04
[ 0.000000] memory.cnt = 0x1
[ 0.000000] memory[0x0] [0x00000030000000-0x00000033ffffff], 0x4000000 bytes flags: 0x0
[ 0.000000] reserved.cnt = 0x3
[ 0.000000] reserved[0x0] [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
[ 0.000000] reserved[0x1] [0x00000030008200-0x0000003057fc03], 0x577a04 bytes flags: 0x0
[ 0.000000] reserved[0x2] [0x00000032000000-0x000000321fffff], 0x200000 bytes flags: 0x0

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

[root@TQ2440 /]# cat /sys/kernel/debug/memblock/memory
: 0x30000000..0x33ffffff
[root@TQ2440 /]# cat /sys/kernel/debug/memblock/reserved
: 0x30004000..0x30007fff
: 0x30008200..0x3057fc03
: 0x32000000..0x321fffff
: 0x33f60000..0x33ffbfff
: 0x33ffc540..0x33ffc96b
: 0x33ffc980..0x33ffc9f7
: 0x33ffca00..0x33ffca03
: 0x33ffca20..0x33ffca23
: 0x33ffca40..0x33ffca43
: 0x33ffca60..0x33ffca63
: 0x33ffca80..0x33ffcad2
: 0x33ffcae0..0x33ffcb32
: 0x33ffcb40..0x33ffcb92
: 0x33ffcba0..0x33ffcbbb
: 0x33ffcbc0..0x33ffcdc7
: 0x33ffcdd0..0x33ffffff

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

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

 #include <linux/module.h>

 #define RESERVE_PHY  0x32000000
#define RESERVE_SIZE 0x200000 static char str[] = "pengdonglin137@163.com\n"; static __init int reserve_demo_init(void)
{
memcpy(phys_to_virt(RESERVE_PHY), str, sizeof(str)); printk("%s: virt: %p\n", __func__, phys_to_virt(RESERVE_PHY)); return ;
} static __exit void reserve_demo_exit(void)
{
printk("%s: %s\n", __func__, (char *)phys_to_virt(RESERVE_PHY));
} module_init(reserve_demo_init);
module_exit(reserve_demo_exit);
MODULE_LICENSE("GPL");

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

 [root@TQ2440 /]# insmod nfs/demo.ko
[ 1417.153362] reserve_demo_init: virt: c2000000
[root@TQ2440 /]#
[root@TQ2440 /]# rmmod demo
[ 1420.986938] reserve_demo_exit: pengdonglin137@.com
[ 1420.986938]

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

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

方法二

修改mach-tq2440.c

 diff --git a/arch/arm/mach-s3c24xx/mach-tq2440.c b/arch/arm/mach-s3c24xx/mach-tq2440.c
index f9679fb..345a868
--- a/arch/arm/mach-s3c24xx/mach-tq2440.c
+++ b/arch/arm/mach-s3c24xx/mach-tq2440.c
@@ -, +, @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/dm9000.h>
+#include <linux/memblock.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -, +, @@ static void __init tq2440_machine_init(void)
s3c_pm_init();
} +static void __init tq2440_reserve(void) {
+ u32 paddr,size;
+
+ printk("%s enter.\n", __func__);
+
+ paddr = 0x32000000;
+ size = 0x200000;
+
+ if (memblock_reserve(paddr, size) < ) {
+ pr_err("failed to reserve DRAM - no memory\n");
+ return;
+ }
+
+ memblock_free(paddr, size);
+ memblock_remove(paddr, size);
+
+ printk("reserve : reserve %dM mem\n", size>>);
+}
+
MACHINE_START(TQ2440, "TQ2440")
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.atag_offset = 0x100, .init_irq = s3c2440_init_irq,
+ .reserve = tq2440_reserve,
.map_io = tq2440_map_io,
.init_machine = tq2440_machine_init,
.init_time = samsung_timer_init,

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

 [    0.000000] Machine: TQ2440
[ 0.000000] memblock_reserve: [0x00000030008200-0x0000003057fc03] flags 0x0 arm_memblock_init+0x4c/0x1bc
[ 0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x158/0x1bc
[ 0.000000] tq2440_reserve enter.
[ 0.000000] memblock_reserve: [0x00000032000000-0x000000321fffff] flags 0x0 tq2440_reserve+0x1c/0x68
[ 0.000000] memblock_free: [0x00000032000000-0x000000321fffff] tq2440_reserve+0x3c/0x68
[ 0.000000] reserve : reserve 2M mem
[ 0.000000] MEMBLOCK configuration:
[ 0.000000] memory size = 0x3e00000 reserved size = 0x57ba04
[ 0.000000] memory.cnt = 0x2
[ 0.000000] memory[0x0] [0x00000030000000-0x00000031ffffff], 0x2000000 bytes flags: 0x0
[ 0.000000] memory[0x1] [0x00000032200000-0x00000033ffffff], 0x1e00000 bytes flags: 0x0
[ 0.000000] reserved.cnt = 0x2
[ 0.000000] reserved[0x0] [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
[ 0.000000] reserved[0x1] [0x00000030008200-0x0000003057fc03], 0x577a04 bytes flags: 0x0

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

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

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

[root@TQ2440 /]# cat /sys/kernel/debug/memblock/reserved
: 0x30004000..0x30007fff
: 0x30008200..0x3057fc03
: 0x33f60000..0x33ffbfff
: 0x33ffc520..0x33ffc94b
: 0x33ffc960..0x33ffc9d7
: 0x33ffc9e0..0x33ffc9e3
: 0x33ffca00..0x33ffca03
: 0x33ffca20..0x33ffca23
: 0x33ffca40..0x33ffca43
: 0x33ffca60..0x33ffcab2
: 0x33ffcac0..0x33ffcb12
: 0x33ffcb20..0x33ffcb72
: 0x33ffcb80..0x33ffcb9b
: 0x33ffcba0..0x33ffcbbb
: 0x33ffcbc0..0x33ffcdc7
: 0x33ffcdd0..0x33ffffff

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

 #include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h> #define RESERVE_PHY 0x32000000
#define RESERVE_SIZE 0x200000 static char str[] = "pengdonglin137@163.com\n";
static void __iomem *addr; static __init int reserve_demo_init(void)
{ if (!request_mem_region(RESERVE_PHY, RESERVE_SIZE, "my reserve"))
return -EBUSY; addr = ioremap_nocache(RESERVE_PHY, RESERVE_SIZE);
memcpy(addr, str, sizeof(str)); printk("%s: virt: %p\n", __func__, addr); return ;
} static __exit void reserve_demo_exit(void)
{
printk("%s: %s\n", __func__, (char *)addr); iounmap(addr);
release_mem_region(RESERVE_PHY, RESERVE_SIZE);
} module_init(reserve_demo_init);
module_exit(reserve_demo_exit);
MODULE_LICENSE("GPL");

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

 [root@TQ2440 /]# insmod nfs/demo.ko
[ 43.853848] reserve_demo_init: virt: c5000000
[root@TQ2440 /]#
[root@TQ2440 /]# rmmod demo
[ 49.247204] reserve_demo_exit: pengdonglin137@.com
[ 49.247204]
[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節點的內容如下:

 [root@TQ2440 /]# cat /d/memblock/memory
: 0x30000000..0x31ffffff
[root@TQ2440 /]#
[root@TQ2440 /]# cat /d/memblock/reserved
: 0x30004000..0x30007fff
: 0x30008200..0x30589c83
: 0x31fa6000..0x31ffbfff
: 0x31ffc840..0x31ffca6b
: 0x31ffca80..0x31ffcaf7
: 0x31ffcb00..0x31ffcb03
: 0x31ffcb20..0x31ffcb23
: 0x31ffcb40..0x31ffcb43
: 0x31ffcb60..0x31ffcb63
: 0x31ffcb80..0x31ffcbda
: 0x31ffcbe0..0x31ffcc3a
: 0x31ffcc40..0x31ffcc9a
: 0x31ffcca0..0x31ffccbb
: 0x31ffccc0..0x31ffcdc3
: 0x31ffcdd0..0x31ffffff

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

 #include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h> #define RESERVE_PHY 0x32000000
#define RESERVE_SIZE 0x200000 static char str[] = "pengdonglin137@163.com\n";
static void __iomem *addr; static __init int reserve_demo_init(void)
{ if (!request_mem_region(RESERVE_PHY, RESERVE_SIZE, "my reserve"))
return -EBUSY; addr = ioremap_nocache(RESERVE_PHY, RESERVE_SIZE);
memcpy(addr, str, sizeof(str)); printk("%s: virt: %p\n", __func__, addr); return ;
} static __exit void reserve_demo_exit(void)
{
printk("%s: %s\n", __func__, (char *)addr); iounmap(addr);
release_mem_region(RESERVE_PHY, RESERVE_SIZE);
} module_init(reserve_demo_init);
module_exit(reserve_demo_exit);
MODULE_LICENSE("GPL");

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

 root@TQ2440 /]# insmod /nfs/demo.ko
[ 266.998897] reserve_demo_init: virt: c3000000
[root@TQ2440 /]#
[root@TQ2440 /]# rmmod demo
[ 271.735731] reserve_demo_exit: pengdonglin137@.com
[ 271.735731]

設置Linux保留物理內存並使用 (1)的更多相关文章

  1. 操作系統3-內存管理(Linux內存管理)

    操作系統3-內存管理(Linux系統的內存管理方法) 9.Linux系統的內存管理方法 Linux採用"按需調頁"算法,支持三層管理策略.由於Intel CPU在硬件級提供了段式存 ...

  2. Linux下安裝Oracle database內核參數設置

    參考:1529864.1 ************************************************** RAM                                  ...

  3. Linux光纖卡配置,磁盤掛載,多路徑設置

    Linux光纖卡配置 1.首先根據光纖卡類型加載對應的驅動.我這裡常用的是QLogic和Brocade光纖卡 [root@rhcsasm2 host3]# lspci | grep Fibre   - ...

  4. rbenv、fish 與 VSCode 設置之路

    在最新的 VSCode 1.3.1 版裡,Integrated Terminal 變得更加好用,但由於上游套件 xterm.js 的緣故,zsh 還是有無法捲動的問題.不過作為一個 Rails 開發者 ...

  5. iPhone的設置——FaceTime頁面

    這裏說的是蘋果的Hand off功能,系統升級後,蘋果的多部設備可以更好的“連續互通”.有電話打進來,iPhone.iPad和Mac都能收到,用戶可以任意選擇一款設備接電 話.同樣,iMessage也 ...

  6. D2GS1.11 的DC Key的相關設置指南

    D2GS1.11版本暗黑戰網服務器DC Key 的相關設置是保存在 D2Server.ini 文件中的.在這裡我列舉跟DC Key 有關的配置條款. (以下內容具存在於D2Server.ini 文件中 ...

  7. Eclipse/MyEclipse 安裝後應該更改的設置

    基本上都通過 Window -> Preferences 進行設置: Java 保存自動格式化: Java:Java -> Editor -> Save Actions,選中 Per ...

  8. 怎樣添加設置GridView,CheckBox全選功能

    GridView內CheckBox控件全選設置 不需要添加後台代碼操作,前端即可完成設置,如下: 前端代碼: 1.設置javascript. <html xmlns="http://w ...

  9. jquery html 獲取或設置

    jquery提供操作html元素的屬性和內容的強大方法. DOM就是獨立于平台和語言的界面,允許程序和腳本動態訪問和改變DOM的內容,結構和樣式. 獲取內容:text(),html(),val(),a ...

随机推荐

  1. *POJ1830 高斯消元

    开关问题 Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 8010   Accepted: 3161 Description ...

  2. Skyshop: Image-Based Lighting Tools & Shaders插件调整反射光不明显的模型

    在Skyshop插件中,使用类似不锈钢等材质的模型,实时反光效果非常好,如果是其他反光不明显的模型,如砖头,建筑等,这时候就需要调整模型的Shader的高光贴图了. 如官方例子中的用砖块组成的柱子,反 ...

  3. nginx 软连接

    ln -s 目标地址 源地址 ln -s ../../../web-admin/etc/nginx-location.conf web-admin.conf

  4. HDFS 架构解析

    本文以 Hadoop 提供的分布式文件系统(HDFS)为例来进一步展开解析分布式存储服务架构设计的要点. 架构目标 任何一种软件框架或服务都是为了解决特定问题而产生的.还记得我们在 <分布式存储 ...

  5. AngularJS2 + ASP.NET MVC项目

    环境:VS2015, NodeJS:v 6.5, npm: v3.10, AngularJs 2 通过将ASP.NET MVC项目与Angualr 2官网上的quick start整合的过程中遇到些问 ...

  6. Socket programing(make a chat software) summary 1:How to accsess LAN from WAN

    First we should know some basic conceptions about network: 1.Every PC is supposed to have its own IP ...

  7. Drupal8重命名上传的中文名文件

    完整的模块代码文件在Coding.net上,想直接使用的请前往下载:https://coding.net/u/yamus/p/chinese_rename/git/tree/master 最近吧Dru ...

  8. iOS----- Crash 分析(文三)- 符号化崩溃日志

    未符号化的崩溃日志就象一本天书,看不懂,更别谈分析崩溃原因了.所以我们在分析日志之前,要把日志翻译成我们可以看得懂的文字.这一步我们称之为符号化. 在iOS Crash分析(文一)中已经提到过符号化的 ...

  9. 编译异常 Caused by: java.lang.UnsupportedClassVersionError:

    Caused by: java.lang.UnsupportedClassVersionError: com/sumingk/platform/service/impl/ServiceSysPerso ...

  10. 神奇的CSS sprites,制作特效的新方法

    本文主要内容简译自Dava Shea的英文文章 CSS Sprites: Image Slicing’s Kiss of Death,如果觉得博主讲的含糊不清的话,可以看作者原文. 熟悉了常规切图的我 ...