内存管理(二)-- linux 预留内存几种方法
日常开发过程可能要预留一段物理内存出来提供特殊场景使用(独占一段内存不被系统所使用)。本文讲解3种预留内存的方法,以及对预留内存的使用。文章目录一、memblock方式预留内存1.1 memblock内存管理1.2 memblock 方式预留内存方法1.3 预留内存访问二、 限制内存总空间方式预留内存2.1 预留内存方法2.2 预留内存访问三、CMA连续内存分配方式预留内存一、memblock方式
日常开发过程可能要预留一段物理内存出来提供特殊场景使用(独占一段内存不被系统所使用)。
本文讲解3种预留内存的方法,以及对预留内存的使用。
文章目录
一、memblock方式预留内存
1.1 memblock内存管理
mmeblock是内存的一种管理机制,主要管理这两种内存:一种是系统可用部分的物理内存(usable),也就是/proc/meminfo里看到的总内存都是提供给系统使用的;另一种是用户预留部分的内存(reserved),用户自己特殊使用,这部分在系统总内存里看不到,本文主要讲解该部分内存怎么预留。
memblock 结构体维护着上述两种内存, 其中成员 memory 维护着可用物理内存区域;成员 reserved 维护预留的内存区域。
struct memblock {
bool bottom_up; /* is bottom up direction? */
phys_addr_t current_limit;
struct memblock_type memory; //维护着可用物理内存区域
struct memblock_type reserved; //维护着预留的内存区域
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
struct memblock_type physmem;
#endif
};
如下这篇对memblock原理讲解的很详细
https://biscuitos.github.io/blog/MMU-ARM32-MEMBLOCK-memblock_reserve/
我们在系统下也可以看到这两种内存的分布情况。
1、dmesg的e820里打印了内存分布情况,可以看到usable和 reserved两种标注的内存块。
- usable:就是系统可用部分的物理内存(System RAM)。
- reserved: 就是预留部分的内存。
[ 0.000000] e820: BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009ebff] usable
[ 0.000000] BIOS-e820: [mem 0x000000000009ec00-0x000000000009ffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000000dc000-0x00000000000fffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x00000000bfecffff] usable
[ 0.000000] BIOS-e820: [mem 0x00000000bfed0000-0x00000000bfefefff] ACPI data
[ 0.000000] BIOS-e820: [mem 0x00000000bfeff000-0x00000000bfefffff] ACPI NVS
[ 0.000000] BIOS-e820: [mem 0x00000000bff00000-0x00000000bfffffff] usable
[ 0.000000] BIOS-e820: [mem 0x00000000f0000000-0x00000000f7ffffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fec00000-0x00000000fec0ffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fffe0000-0x00000000ffffffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000013fffffff] usable
2、或者在cat /proc/iomem |grep 'System RAM’的 io内存分布里也能看到系统可用部分的物理内存的分布情况。4段System RAM加起来就是总的可用物理内存。
[root@localhost ~]# cat /proc/iomem |grep 'System RAM'
00001000-0009ebff : System RAM
00100000-bfecffff : System RAM
bff00000-bfffffff : System RAM
100000000-13fffffff : System RAM
1.2 memblock 方式预留内存方法
方法很简单,通过memblock_reserve() 将一段物理内存放在预留区里,在setup.c中setup_arch启函数中添加,内核补丁如下(基于:linux3.10.0-123.):
diff -uNrp a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
--- a/arch/x86/kernel/setup.c 2021-01-29 23:09:08.443072526 -0800
+++ b/arch/x86/kernel/setup.c 2021-01-29 23:31:53.521307672 -0800
@@ -907,6 +907,8 @@ static void rh_check_supported(void)
void __init setup_arch(char **cmdline_p)
{
+
+ struct memblock_region *reg;
memblock_reserve(__pa_symbol(_text),
(unsigned long)__bss_stop - (unsigned long)_text);
@@ -1035,6 +1037,13 @@ void __init setup_arch(char **cmdline_p)
* again from within noexec_setup() during parsing early parameters
* to honor the respective command line option.
*/
+
+ memblock_reserve(0x100000000, 0x2000000);
+ pr_info("Scan equal region:\n");
+ for_each_memblock(reserved, reg) //遍历打印reserved所有分区
+ pr_info("Region [%llx -- %llx]\n", (u64)(reg->base),
+ (u64)(reg->base + reg->size));
+
x86_configure_nx();
parse_early_param();
memblock_reserve(0x100000000, 0x2000000);函数完成了对ram起始地址0x100000000开始保留32MB(0x2000000)内存。reserved链表管理着预留区域的内存,memblock_reserve会将预留内存段插入链表新节点里(如果出现reserve之间地址相互覆盖的,reserver会将它们合并成一个内存区,即一个节点)。for_each_memblock()可以获取每个预留区内存信息并打印。
该方式是否成功预留出内存,可以在dmesg中查看打印,如果要查看memblock调试打印信息,可以在grub中加入:memblock=debug。
但实际测试,在e820和iomem里并没有显现该区域内存信息。猜测是memblock_reserve()没有把内存块纳入e820管理中,有待研究。
1.3 预留内存访问
通常访问指定物理地址的内存方式有很多种,如:
memremap / ioramp 方式将其映射
phy_to_vir 线性映射虚拟地址
mmap方式将物理地址映射到用户空间
通过实际测试, memblock_reserve保留的内存段,可以采用memremap ,phy_to_vir 方式将物理地址映射出并使用。
二、 限制内存总空间方式预留内存
2.1 预留内存方法
该方法比较简单,直接修改系统grub启动参数,将系统总内存限制在指定大小,使得其余内存不可见。然后对不可见内存进行申请保留。
[root@localhost ~]# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.10.0-123.el7.x86_64 root=UUID=79704805-e306-420b-827a-52849e1376c1 ro vconsole.keymap=us crashkernel=auto vconsole.font=latarcyrheb-sun16 rhgb quiet LANG=en_US.UTF-8 mem=1G
如上mem=2G ,是限制了可用物理内存为2G,那么我们可以对[2-4G]内的内存进行申请,映射和使用。
在iomem和meminfo里可以看到系统可用总内存已经减少2G左右
[root@localhost ~]# cat /proc/iomem |grep RAM
00001000-0009ebff : System RAM
00100000-3fffffff : System RAM
100000000-13fffffff : System RAM
e820里可以显示到全部(包括隐藏)的内存情况,usable是对应的物理内存,比对iomem中的RAM部分,可知隐藏掉的部分(预留)内存为:[3fffffff—bfecffff], [bff00000–bfffffff]。 大约2G左右
[root@localhost ~]# dmesg |grep e820
[ 0.000000] e820: BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009ebff] usable
[ 0.000000] BIOS-e820: [mem 0x000000000009ec00-0x000000000009ffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000000dc000-0x00000000000fffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x00000000bfecffff] usable
[ 0.000000] BIOS-e820: [mem 0x00000000bfed0000-0x00000000bfefefff] ACPI data
[ 0.000000] BIOS-e820: [mem 0x00000000bfeff000-0x00000000bfefffff] ACPI NVS
[ 0.000000] BIOS-e820: [mem 0x00000000bff00000-0x00000000bfffffff] usable
[ 0.000000] BIOS-e820: [mem 0x00000000f0000000-0x00000000f7ffffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fec00000-0x00000000fec0ffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fffe0000-0x00000000ffffffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000013fffffff] usable
2.2 预留内存访问
对预留部分内存访问的驱动代码如下,从0x40000000开始,申请512MB(0x20000000)内存。
static char data[] = "123456\n";
static void* addr;
static int __init ram_reserve_init(void)
{
if (!request_mem_region(0x40000000, 0x20000000, "reserve test")) //请求不可见的内存段权限(即:检查你申请的资源是否可用,如果可用,则将其标志为被使用。非必须)
{
printk("request_mem_region fail\n");
return - EBUSY;
}
addr = memremap(0x40000000, 0x20000000, MEMREMAP_WB); //映射内存空间
memcpy(addr, data, sizeof(str)); //拷贝测试数据
printk( "%s: %s\n " , __func__, ( char * )addr);//读出内存数据
return 0;
}
static void __exit ram_reserve_exit(void)
{
iounmap(addr);
release_mem_region(RESERVE_PHY, RESERVE_SIZE);
pr_info(KBUILD_MODNAME ": unloaded.\n");
}
module_init(ram_reserve_init);
module_exit(ram_reserve_exit);
request_mem_region() 检查你申请的资源是否可用,如果可用,则将其标志为被使用,如果该内存区已经被标记,会返回报错,该调用可以忽略,建议加上。加载驱动后,通过/proc/iomem 可以查看到reserve test段的内存分配信息
该方式有个缺点就是预留的内存是ram空间的尾部,边界不确定性,可能无法准确预留出指定大小的内存。
通过实际测试,不可见内存段保留方式,不能通过phy_to_vir 方式进行映射,因为phy_to_vir 只限在normal区的内存,不可见的内存段在系统认为不在normal区中,但可以采用memremap访问 。
三、CMA连续内存分配方式预留内存
cma(contiguous memory allocation)是linux中一种动态分配连续物理内存的方式,可认为是传统kmalloc的升级版,伙伴管理系统中,kmalloc最大只能申请4M内存,开启了cma后可以申请大块的连续内存区域,该方式是先将cma部分内存从系统预留出来,当要使用时单独从cma里面动态申请大小,因此申请时候不能指定其申请地址。cma机制需要修改内核编译参数(或修改设备树)进行部署才能使用,篇幅较长,后续文章中讲解。
更多推荐
所有评论(0)