ARM32内存空间分配

2023-10-11 10:40
文章标签 分配 内存空间 arm32

本文主要是介绍ARM32内存空间分配,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux内核一般将处理器的虚拟地址空间分成两部分,在32系统上,地址空间在用户进程和内核之间划分的典型比例为3:1,在给出的4GB的虚拟地址空间中,0 ~ 3GB将用于用户空间而3GB ~ 4GB将用于内核空间,内核提供了相关的配置项来修改该比例,也就是说Kernel最多寻址1GB的虚拟地址空间。

当CPU启动MMU后,CPU访问的时虚拟地址空间,然后由MMU根据页表转换成物理地址,页表是由Kernel维护的,所以Kernel可以决定1GB的虚拟地址空间具体映射到什么物理地址。但是不管Kernel怎么映射,最多也只能映射1G的物理内存,所以如果一个系统有超过1G的物理内存,在某一时刻,必然有一部分空间是内核无法访问到的,对于该问题内核借助于高端内存(highmem)方法来管理多余的内存,本章的主要讲解以下内容

什么是高端内存及其作用
ARM32 Linux的内存布局
1. 什么是高端内存
对于32系统,内核使用3G ~ 4G的虚拟地址空间,那么只有1G的地址空间可以用来映射物理空间。但是,如果我们使用的内存大于1G的情况,是不是超过1G的内存就无法使用了呢?为此内核引入了一个高端内存的概念,把1G的虚拟地址空间分成两部分

低端内存空间:小于high_memory的物理地址空间,这部分的内存物理地址和3G开始的线性地址是一一映射的,也就是说内核使用的线性地址空间3G ~ (3G + high_memory)和物
理地址空间0 ~ high_memory一一映射
高端内存空间:剩下的128M的线性空间用来映射剩下的大于high_memory的物理地址空间
对于以上,我们可以知道以下问题

对于高端内存high_memory,现在一般是896M,当我们只有512M的内存的时候,就没有高端内存一说了,因为512MB的物理内存已经被kernel直接映射
64位系统下不会有high memory,因为64位虚拟地址空间非常大(分给kernel的也很大),完全能够直接映射全部物理内存。
在32位系统上,没有任何进程能够有效地使用超过3GB的内存。这意味着购买超过4GB的从理论上是发挥不出其优势
对于我们使用的IMX6U,由于使用了CMA,所以其高端内存high_memory的地址空间范围为0xe000 0000 ~ 0xFFFF FFFF(512 MB ~ 1024MB)。那么内核如何借助512M高端内存地址空间实现访问访问所有4GB的物理内存。比如当内核项访问高于High_memory的物理地址时,可以从(0xc000 0000 + high_memory) ~ 0xFFFF FFFF地址空间范围内找一段相应大小的空闲虚拟地址,建立映射到想访问的那段物理内存,用完后归还。这样就所有的人都可以借用这段地址空间访问物理地址,访问所有物理内容如下图所示:

在这里插入图片描述

我们可以知道高端内存的最基本思想:借一段地址空间,建立临时地址映射,用完后释放,达到这段地址空间可以循环使用,访问所有物理内存。万一有内核进程或模块一直占用某段逻辑地址空间不释放,怎么办?若真的出现的这种情况,则内核的高端内存地址空间越来越紧张,若都被占用不释放,则没有建立映射到物理内存都无法访问了。

2. Linux内核高端内存的划分
对于高端内存,一般划分如下:

动态内存映射区:虚拟内存中连续,但物理内存不连续的内存,可以在vmalloc区域分配。该机制通常用于用户空间,内核自身会试图尽力避免非连续的物理内存。
永久内存映射区:该区域可访问高端内存,访问方法是使用alloc_page(_GFP_HIGHMEM)分配高端内存页或使用kmap函数将分配到高端内存映射到该区域
固定映射区:固定映射是与物理内存空间中的固定页关联的虚拟地址空间项,该区域和4GB的顶端只有4K的隔离带,其每个地址项都服务于特定的用途。
对于高端内存的划分,其中fixed mapping主要用在boot阶段用来永久性映射一些物理地址固定的数据结构或者硬件地址(比如ACPI表,APIC地址,等等)。kmap area是kernel用来临时建立映射来访问物理页用的,可用的地址空间也比较小。绝大部分reserve了给vmalloc area,vmalloc和ioremap返回的都是这个空间里的地址。

3. ARM32内存分布图
了解完低端内存和高端内存的概念,我们来看看我们实际上内存布局是怎么样的?Linux内核在启动时,会打印出内核内存空间的布局图,下面是ARM IMX6平台打印出来的内存空间布局图

Virtual kernel memory layout:
     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
     vmalloc : 0xe0800000 - 0xff800000   ( 496 MB)
     lowmem  : 0xc0000000 - 0xe0000000   ( 512 MB)
     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
       .text : 0xc0008000 - 0xc0a00000   (10208 kB)
       .init : 0xc0e00000 - 0xc1000000   (2048 kB)
       .data : 0xc1000000 - 0xc1074c00   ( 467 kB)
        .bss : 0xc1076000 - 0xc10e8eec   ( 460 kB)
1
2
3
4
5
6
7
8
9
10
11
这部分信息是的打印是在mem_init()函数中实现的

    pr_notice("Virtual kernel memory layout:\n"
            "    vector  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
#ifdef CONFIG_HAVE_TCM
            "    DTCM    : 0x%08lx - 0x%08lx   (%4ld kB)\n"
            "    ITCM    : 0x%08lx - 0x%08lx   (%4ld kB)\n"
#endif
            "    fixmap  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
            "    vmalloc : 0x%08lx - 0x%08lx   (%4ld MB)\n"
            "    lowmem  : 0x%08lx - 0x%08lx   (%4ld MB)\n"
#ifdef CONFIG_HIGHMEM
            "    pkmap   : 0x%08lx - 0x%08lx   (%4ld MB)\n"
#endif
#ifdef CONFIG_MODULES
            "    modules : 0x%08lx - 0x%08lx   (%4ld MB)\n"
#endif
            "      .text : 0x%p" " - 0x%p" "   (%4td kB)\n"
            "      .init : 0x%p" " - 0x%p" "   (%4td kB)\n"
            "      .data : 0x%p" " - 0x%p" "   (%4td kB)\n"
            "       .bss : 0x%p" " - 0x%p" "   (%4td kB)\n",
            
            MLK(UL(CONFIG_VECTORS_BASE), UL(CONFIG_VECTORS_BASE) +
                (PAGE_SIZE)),
#ifdef CONFIG_HAVE_TCM
            MLK(DTCM_OFFSET, (unsigned long) dtcm_end),
            MLK(ITCM_OFFSET, (unsigned long) itcm_end),
#endif
            MLK(FIXADDR_START, FIXADDR_END),
            MLM(VMALLOC_START, VMALLOC_END),
            MLM(PAGE_OFFSET, (unsigned long)high_memory),
#ifdef CONFIG_HIGHMEM
            MLM(PKMAP_BASE, (PKMAP_BASE) + (LAST_PKMAP) *
                (PAGE_SIZE)),
#endif
#ifdef CONFIG_MODULES
            MLM(MODULES_VADDR, MODULES_END),
#endif

            MLK_ROUNDUP(_text, _etext),
            MLK_ROUNDUP(__init_begin, __init_end),
            MLK_ROUNDUP(_sdata, _edata),
            MLK_ROUNDUP(__bss_start, __bss_stop));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
内核image本身占据的内存空间从_text段到 _end段,并且分为如下几段

代码段:_text和 _etext为代码段的起始和结束地址,包含了编译后的内核代码
init 段:_init_begin 和 _init_end为init段的起始和结束地址,包含了大部分模块初始化的数据
数据段:_sdata和 _edata数据段的起始和结束地址,保存大部分内核的变量
BSS段:bss_start和 _bss_stop为BSS段的开始和结束地址,包含了初始化为0的所有的静态全局变量
那么高端内存的起始地址是如何确定的呢?在内核初始化内存时候,在adjust_lowmem_bounds函数中确定低端内存和高端内存的起始地址

vmalloc_limit = (u64)(uintptr_t)vmalloc_min - PAGE_OFFSET + PHYS_OFFSET;    
arm_lowmem_limit = lowmem_limit;
high_memory = __va(arm_lowmem_limit - 1) + 1;
1
2
3
计算出来内核线性映射512M地址空间,剩下的保留给vmalloc,fixmap和高端向量表使用,内核很多驱动使用vmalloc来分配连续虚拟地址内存,因为有的驱动不需要连续物理地址内存;除此之外,vmalloc还可以用于高端内存的临时映射。对于内核vmalloc空间配置如下

#define VMALLOC_OFFSET        (8*1024*1024)
#define VMALLOC_START        (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#define VMALLOC_END        0xff800000UL
1
2
3
vmalloc区域在ARM32内核中,从VMALLOC_START开始到VMALLOC_END结束,即从0xe0800000 - 0xff800000,大小为512MB,在VMALLOC_START开始之前有一个8MB的空间,用于捕获越界访问。

4. 总结
对于高端内存,首先我们需要明确这是一个物理内存的概念,它仅仅是内核中的内存管理模块看待物理内存的时候的概念。在内核中,除了内存管理模块直接操作物理地址之外,其他的模块,都需要操作虚拟地址,而虚拟地址是需要内存管理模块分配和映射的。例如,我们有一个2G的内存,现在内核模块如果想要访问物理内存1.5G的地方,应该怎么办呢?

首先,我们不能使用物理地址,你需要使用内存管理模块给你分配虚拟地址,但是虚拟地址0 ~ 3G已经被用户态进程占用去了,作为内核不能使用。
其次,对于1.5G的地方就算映射了,也不是你真正要访问的物理内存地址,所以对于内核,能够使用虚拟内存地址,只剩下高端内存这块了。
内核通常把物理内存低于high_memory的地址成为线性映射地址,而高于high_memory以上的成为高端内存。由于32位系统寻址能力只有4GB,对于物理内存高于high_memory而低于4GB的情况,我们就需要从虚拟地址空间中画出一部分来用于动态映射高端内存,这样就可以访问到全部的4GB的内存。而对于映射高端内存的虚拟地址空间,可以划分位固定映射区和临时映射区,后面章节中单独来讲解每个的功能。对于我们现在使用的IMX6U的内核空间的内存分布图如下所示,对于途中的高端内存每个平台可能都不一样,主要是通过客户实际需要来配置

在这里插入图片描述

这篇关于ARM32内存空间分配的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/187488

相关文章

string字符会调用new分配堆内存吗

gcc的string默认大小是32个字节,字符串小于等于15直接保存在栈上,超过之后才会使用new分配。

操作系统是怎么为不同的程序分配所需的内存空间的

操作系统为不同的程序分配内存空间的过程涉及多个关键步骤,确保每个程序都有其所需的内存资源,同时避免程序之间的冲突。以下是操作系统如何为程序分配内存空间的详细过程: 1. 内存管理的基础概念 虚拟内存:现代操作系统使用虚拟内存机制来为程序提供隔离的内存空间。每个程序运行在其独立的虚拟地址空间中,这使得程序间的内存互不干扰。物理内存:实际的 RAM(随机存取存储器),由操作系统和硬件共同管理。虚拟

利用PL/SQL工具如何给指定用户分配权限

选中指定的表--右键--编辑--就出现右边的内容了,选择权限,分配用户某个权限就行了;

【go】内存分配模型

内存是怎么分配给对象的? 内存分配优化的地方是? 讲讲golang内存分配模型? ans: 1.按照对象的大小分配:先算出对象的大小如果是tiny对象,就从tiny block中获取地址和偏移量,将对象打包到mcache;如果是16B以上32k以内就先从mcache获得对应class级别的span;如果mcache没有就从mcenter中获取,如果mcenter没有就创建一个mspan从

SimD:基于相似度距离的小目标检测标签分配

摘要 https://arxiv.org/pdf/2407.02394 由于物体尺寸有限且信息不足,小物体检测正成为计算机视觉领域最具挑战性的任务之一。标签分配策略是影响物体检测精度的关键因素。尽管已经存在一些针对小物体的有效标签分配策略,但大多数策略都集中在降低对边界框的敏感性以增加正样本数量上,并且需要设置一些固定的超参数。然而,更多的正样本并不一定会带来更好的检测结果,事实上,过多的正样本

c语言(三种语言对比、数据类型及输出、变量存储、内存分配)

数据类型: 变量的输出: c语言不能直接打印数字,字符,只能将他们转化成字符串打印, printf("%d\n", num); %d 是整数占位符,将来由逗号右边的变量的值占据这个位置。(%i和%d是一样的) %f 是浮点数占位符,默认输出小数点后6位,若只想输出小数点后两位:%.2f %c 是字符型数据占位符。

Kafka【十二】消费者拉取主题分区的分配策略

【1】消费者组、leader和follower 消费者想要拉取主题分区的数据,首先必须要加入到一个组中。 但是一个组中有多个消费者的话,那么每一个消费者该如何消费呢,是不是像图中一样的消费策略呢?如果是的话,那假设消费者组中只有2个消费者或有4个消费者,和分区的数量不匹配,怎么办? 所以这里,我们需要学习Kafka中基本的消费者组中的消费者和分区之间的分配规则: 同一个消费者组的消费者都订

C语言行地址列地址区别,内存的分配

开辟了10个字节的空间在栈中     "abc"常量在代码段 char a[10]="abc"; p:8个字节存"abc"的地址                "abc"常量在代码段中 char *p="abc" char *q="abc"              p,q的值是一样的 到代码段中找"abc"直接取地址 //

Anchor Alignment Metric来优化目标检测的标签分配和损失函数。

文章目录 背景假设情况任务和目标TaskAligned方法的应用1. **计算Anchor Alignment Metric**2. **动态样本分配**3. **调整损失函数** 示例总结 背景 假设我们在进行目标检测任务,并且使用了YOLOv8模型。我们希望通过TaskAligned方法来优化Anchor与目标的匹配程度,从而提升检测效果。 假设情况 图像: 一张包含

Flink on YARN模式下TaskManager的内存分配探究

点击上方蓝色字体,选择“设为星标” 回复”资源“获取更多资源 我们使用如下的参数提交了Flink on YARN作业(per-job模式)。 /opt/flink-1.9.0/bin/flink run \--detached \--jobmanager yarn-cluster \--yarnname "x.y.z" \--yarnjobManagerMemory 2048 \--