linux内核无法识别、无法使用全部物理内存

目录

1 简介

2 实际物理内存大小 和 linux内核识别到的内存大小

3 引导内存分配器——memblock

4 内核地址空间布局中的 线性映射区域

5 arm64_memblock_init()函数

5.1 函数代码

5.2 linear_region_size值

5.3 在使用不同的页大小时,上面5.1小结代码对应的dmesg信息

6 总结


目录

1 简介

2 实际物理内存大小 和 linux内核识别到的内存大小

3 引导内存分配器——memblock

4 内核地址空间布局中的 线性映射区域

5 arm64_memblock_init()函数

5.1 函数代码

5.2 linear_region_size值

5.3 在使用不同的页大小时,上面5.1小结代码对应的dmesg信息

1 简介

        在本人实际工作过程中,遇到了2台机器,当linux内核(kernel-5.4)使用的页大小(PAGE_SIZE)为4K时无法识别、无法使用全部的物理内存。将虚拟地址位宽(CONFIG_ARM64_VA_BITS)改成48,可以解决问题,本文将会对此进行机理分析。

2 实际物理内存大小 和 linux内核识别到的内存大小

实际物理内存/proc/meminfo下看到的(MemTotal:)内存
机器132G6G多
机器2128G14G多

3 引导内存分配器——memblock

        在内核初始化的过程中需要分配内存,内核提供了临时的引导内存分配器,在页分配器和块分配器初始化完毕后,把空闲的物理页交给页分配器管理,丢弃引导内存分配器。

        早期使用的引导内存分配器是bootmem,目前正在使用memblock取得bootmem。  

                                                                                                                                           《Linux内核深度解析》P144

在内核初始化时,会将全部物理内存添加到memblock,dmesg信息(需要在启动参数里加上:memblock=debug)如下

[    0.000000] memblock_add: [0x0000000000000000-0x000000000007ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000000000080000-0x000000000181ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000000001820000-0x000000001fffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000000080000000-0x0000000087ffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000000088000000-0x0000000088ffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000000089000000-0x00000000aec1afff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000aec1b000-0x00000000aec50fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000aec51000-0x00000000afffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000b0000000-0x00000000b8ffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000b9000000-0x00000000b900ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000b9010000-0x00000000e86f4fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000e86f5000-0x00000000ef05ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000ef060000-0x00000000ef19ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000ef1a0000-0x00000000ef25ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_reserve: [0x00000000ef1a0000-0x00000000ef25ffff] reserve_regions+0x14c/0x18c
[    0.000000] memblock_add: [0x00000000ef260000-0x00000000ef26ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000ef270000-0x00000000ef27efff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000ef27f000-0x00000000f2e7ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f2e80000-0x00000000f2f8ffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f2f90000-0x00000000f2faffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_reserve: [0x00000000f2f90000-0x00000000f2faffff] reserve_regions+0x14c/0x18c
[    0.000000] memblock_add: [0x00000000f2fb0000-0x00000000f33affff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f33b0000-0x00000000f33e8fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f33e9000-0x00000000f33e9fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f33ea000-0x00000000f33eafff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f33eb000-0x00000000f33effff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f33f0000-0x00000000f37cefff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f37cf000-0x00000000f37effff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f37f0000-0x00000000f4e47fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f4e48000-0x00000000f4ec7fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f4ec8000-0x00000000f4eccfff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f4ecd000-0x00000000f4ed0fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f4ed1000-0x00000000f4eecfff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f4eed000-0x00000000f5066fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f5067000-0x00000000f5067fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f5068000-0x00000000f5a34fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f5a35000-0x00000000f5a42fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f5a43000-0x00000000f5a50fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f5a51000-0x00000000f5a91fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f5a92000-0x00000000f77effff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f77f0000-0x00000000f8fb2fff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000f8fb3000-0x00000000fb7effff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000fb7f0000-0x00000000fbbeffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000fbbf0000-0x00000000fbfeffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000fbff0000-0x00000000fbffefff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000000fbfff000-0x00000000fbffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000000100000000-0x00000003ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000008000000000-0x000000839bffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x000000839c000000-0x00000083ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000020000000000-0x00000203ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000028000000000-0x00000283ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000040000000000-0x00000403ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000048000000000-0x00000483ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000060000000000-0x00000603ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000068000000000-0x00000683ffbfffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000683ffc00000-0x00000683ffdfffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000683ffe00000-0x00000683fffeffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x00000683ffff0000-0x00000683ffffffff] early_init_dt_add_memory_arch+0x50/0x58

4 内核地址空间布局中的 线性映射区域

 《Linux内核深度解析》P121

PAGE_OFFSET表示物理内存在内核地址空间里做线性映射的起始地址。

Linux内核在初始化时会把物理内存全部做一次线性映射,映射到内核空间的虚拟地址上。

《奔跑吧Linux内核》卷1:基础架构,P75

arm64_memblock_init()函数中,把线性映射区不能覆盖的物理内存范围从memblock.memory中删除。

《Linux内核深度解析》P147

5 arm64_memblock_init()函数

5.1 函数代码

void __init arm64_memblock_init(void)
{
    ......
    /*
     * Remove the memory that we will not be able to cover with the
     * linear mapping. Take care not to clip the kernel which may be
     * high in memory.
     */
    memblock_remove(max_t(u64, memstart_addr + linear_region_size,
            __pa_symbol(_end)), ULLONG_MAX);
    ......
}

当前系统下memstart_addr的值是0。

5.2 linear_region_size值

根据上面第4小结图片里的信息,linear_region_size = 0xFFFFFFFFFFFFFFFF - (0xFFFFFFFFFFFFFFFF << (VA_BITS -1))。

当内核使用4K页时,VA_BITS(CONFIG_ARM64_VA_BITS)默认值是39。

        linear_region_size = 0xFFFFFFFFFFFFFFFF - (0xFFFFFFFFFFFFFFFF << (39 -1)) + 1
                                      = 0xFFFFFFFFFFFFFFFF - 0xffffffc000000000 + 1
                                      = 0x3FFFFFFFFF + 1
                                      = 0x4000000000

当内核使用64K页时,修改VA_BITS(CONFIG_ARM64_VA_BITS)的值为48。

        linear_region_size = 0xFFFFFFFFFFFFFFFF - (0xFFFFFFFFFFFFFFFF << (48 -1)) + 1
                                      = 0xFFFFFFFFFFFFFFFF - ffff800000000000 + 1
                                      = 0x7FFFFFFFFFFF + 1
                                      = 0x800000000000

5.3 在使用不同的页大小时,上面5.1小结代码对应的dmesg信息

4K页,VA_BITS(CONFIG_ARM64_VA_BITS)默认值是39,linear_region_size值是0x4000000000

[    0.000000] memblock_remove: [0x0001000000000000-0x0000fffffffffffe] arm64_memblock_init+0x7c/0x3dc
[    0.000000] memblock_remove: [0x0000004000000000-0x0000003ffffffffe] arm64_memblock_init+0xd0/0x3dc    //把线性映射区不能覆盖的物理内存范围从memblock.memory中删除。
[    0.000000] memblock_remove: [0x00000000ead29000-0x00000000edbf4fff] arm64_memblock_init+0x190/0x3dc

4K页,修改VA_BITS(CONFIG_ARM64_VA_BITS)的值为48,linear_region_size值是0x800000000000

[    0.000000] memblock_remove: [0x0001000000000000-0x0000fffffffffffe] arm64_memblock_init+0x7c/0x3dc
[    0.000000] memblock_remove: [0x0000800000000000-0x00007ffffffffffe] arm64_memblock_init+0xd0/0x3dc    //把线性映射区不能覆盖的物理内存范围从memblock.memory中删除。
[    0.000000] memblock_remove: [0x00000000ead28000-0x00000000edbf4fff] arm64_memblock_init+0x190/0x3dc

6 总结

        在linux内核初始化时,会将所有物理内存的地址空间添加到memblock.memory里面。需要注意的是,物理内存的地址空间并不是连续的,比如第3小结里的信息

[    0.000000] memblock_add: [0x0000000100000000-0x00000003ffffffff] early_init_dt_add_memory_arch+0x50/0x58
[    0.000000] memblock_add: [0x0000008000000000-0x000000839bffffff] early_init_dt_add_memory_arch+0x50/0x58

        之后,内核会将线性映射区域无法覆盖的物理内存地址空间从memblock.memory中删除。线性映射区域的大小是由虚拟地址的位宽(CONFIG_ARM64_PA_BITS)决定的。

  •         当使用4K页时,默认的虚拟地址位宽是39,对应的线性映射区域大小为0x4000000000;
  •         当使用64K页时,修改虚拟地址位宽是48,对应的线性映射区域大小为0x800000000000;

        当虚拟地址位宽是39时,线性映射区域大小是0x4000000000,也就是说,物理内存地址如果高于0x4000000000,linux内核是无法识别和使用的。根据第3小结的信息,有大量的物理内存地址大于0x4000000000,所以这时候linux内核无法识别和使用全部的物理内存。

        根据第3小结中的信息,最大的物理内存地址是0x683ffffffff ,小于线性映射区域大小(0x800000000000),所以当虚拟地址位宽是48时,linux内核可以识别、使用全部的物理内存。

参考:

《Linux内核深度解析》P121

《奔跑吧Linux内核》卷1:基础架构,P75

《Linux内核深度解析》P147

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值