最近在做系统剪裁,发现可见物理内存为103MB(128M),其中有24M内存消失了,表示对这一现象很是奇怪,下面讲述了我找这24MB的心路历程。

目标板子配置:
板子为ARM架构、板子上有256MB内存,内核版本为4.9.54
Linux version 4.9.54 (jenkins@localhost.localdomain) (gcc version 6.3.1 20170109 (Linaro GCC 6.3-2017.02) ) #8 SMP PREEMPT Fri Jul 27 17:29:51 HKT 2018

为了避免由于内存硬件差异导致的内存差异,(这一点我还真不清楚,真实的可用物理内存,比标签的内存要小点,小多少我不知道),在dts设置中,设定给系统内存设置为128M,设置如下

    memory@00000000 {
        device_type = "memory";
        linux,usable-memory = <0x0 0x000000 0x0 0x8000000>; 
    };
重新编译后,进入系统,查看系统内存使用情况如下,系统可用物理内存为105600KB,即为103MB(使用htop更能明显的看出来)

 cat /proc/meminfo 
MemTotal:         105600 kB
MemFree:            2732 kB
.......
CmaTotal:           8192 kB
CmaFree:               0 kB

# free
             total       used       free     shared    buffers     cached
Mem:        105600     102332       3268        116          0      29740
-/+ buffers/cache:      72592      33008
Swap:            0          0          0

从这里可以发现,系统的可见物理内存只有103MB,还有128MB - 103MB = 24MB不知道去哪里了。
然后网上查查查查,发现系统kernel和用户层的内存是不能相互可见的,都是相对独立的,想在用户层访问kernel的内存,可以通过某些方式把这块内存的地址读取上来(还有更多方法,不多介绍)。然后就去找kernel的二进制文件vmlinux.o,使用size和objdump查看此文件

 size vmlinux
   text    data     bss     dec     hex filename
8423164 2480188  509012 11412364     ae238c vmlinux 

test为代码段,data和bss也都是放在内存的段,data是放初始化了的全局静态变量和局部静态变量,bss是放未初始化的全局变量和局部静态变量,bss也是会提前在内存中预留空间,所以这三段在kernel运行时候都是在内存中分配的,计算得
(8423164 + 2480188 + 509012) / 1024 / 1024.0 = 10.8828125MB

这大约11MB的内存是kernel在运行时候被划分给kernel的,用户层无法访问,对用户层也是不可见的,所以使用htop、free看到的可用物理内存是没有这一部分的,24MB - 11MB = 13MB,然而还有13MB不知道是哪里拿走了。

接着网上查查查查,发现是系统预留了,但是网上没有找到具体是什么预留怎么预留的,继续研究,发现dts文件中有 reserved-memory的关键字,有两部分ramoops预留了1M,secmon_reserved分配了4M内存,这部分内存对用户层也是不可见的。

    memory@00000000 {
        device_type = "memory";
        linux,usable-memory = <0x0 0x000000 0x0 0x8000000>; 
    };

    reserved-memory {
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;
        /* global autoconfigured region for contiguous allocations */
        ramoops@0x07400000 {
                compatible = "ramoops";
                reg = <0x0 0x07400000 0x0 0x00100000>;
                record-size = <0x8000>;
                console-size = <0x8000>;
                ftrace-size = <0x0>;
                pmsg-size = <0x8000>;
        };
        secmon_reserved:linux,secmon {
            compatible = "shared-dma-pool";
            reusable;
            size = <0x0 0x400000>;
            alignment = <0x0 0x400000>;
            alloc-ranges = <0x0 0x05000000 0x0 0x400000>;
        };

然后接着查查查查,仔细的查看了系统开机启动的dmesg log,截取一部分如下

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.9.54 (jenkins@localhost.localdomain) (gcc version 6.3.1 20170109 (Linaro GCC 6.3-2017.02) ) #8 SMP PREEMPT Fri Jul 27 17:29:51 HKT 2018
[    0.000000] Boot CPU: AArch64 Processor [410fd034]
[    0.000000] earlycon: aml_uart0 at MMIO 0x00000000ff803000 (options '')                                                          
[    0.000000] bootconsole [aml_uart0] enabled
[    0.000000] efi: Getting EFI parameters from FDT: 
[    0.000000] efi: UEFI not found.
[    0.000000] Reserved memory: created CMA memory pool at 0x0000000005000000, size 4 MiB
[    0.000000] OF: reserved mem: initialized node linux,secmon, compatible id shared-dma-pool
[    0.000000] cma: Reserved 4 MiB at 0x0000000007c00000
[    0.000000] On node 0 totalpages: 33453
[    0.000000]   DMA zone: 523 pages used for memmap
[    0.000000]   DMA zone: 0 pages reserved
[    0.000000]   DMA zone: 33453 pages, LIFO batch:7
[    0.000000] PID hash table entries: 1024 (order: 1, 8192 bytes)
[    0.000000] Dentry cache hash table entries: 32768 (order: 6, 262144 bytes)
[    0.000000] Inode-cache hash table entries: 16384 (order: 5, 131072 bytes)
[    0.000000] Memory: 93200K/133812K available (6588K kernel code, 462K rwdata, 2204K rodata, 1472K init, 498K bss, 32420K reserved, 8192K cma-reserved)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     modules : 0xffffff8000000000 - 0xffffff8008000000   (   128 MB)
[    0.000000]     vmalloc : 0xffffff8008000000 - 0xffffffbebfff0000   (   250 GB)
[    0.000000]       .text : 0xffffff8009080000 - 0xffffff80096f0000   (  6592 KB)
[    0.000000]     .rodata : 0xffffff80096f0000 - 0xffffff8009920000   (  2240 KB)
[    0.000000]       .init : 0xffffff8009920000 - 0xffffff8009a90000   (  1472 KB)
[    0.000000]       .data : 0xffffff8009a90000 - 0xffffff8009b03a00   (   463 KB)
[    0.000000]        .bss : 0xffffff8009b03a00 - 0xffffff8009b80454   (   499 KB)
[    0.000000]     fixed   : 0xffffffbefe7fd000 - 0xffffffbefec00000   (  4108 KB)
[    0.000000]     PCI I/O : 0xffffffbefee00000 - 0xffffffbeffe00000   (    16 MB)
[    0.000000]     vmemmap : 0xffffffbf00000000 - 0xffffffc000000000   (     4 GB maximum)
[    0.000000]               0xffffffbf00000000 - 0xffffffbf003bec80   (     3 MB actual)
[    0.000000]     memory  : 0xffffffc000000000 - 0xffffffc00efb2000   (   239 MB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1

[    0.145584] pstore: using zlib compression
[    0.145610] console [pstore-1] enabled
[    0.145818] pstore: Registered ramoops as persistent store backend
[    0.145826] ramoops: attached 0x100000@0x7400000, ecc: 0/0  

可以看到其中有4行关键的信息如下:
[ 0.000000] Reserved memory: created CMA memory pool at 0x0000000005000000, size 4 MiB
[ 0.000000] OF: reserved mem: initialized node linux,secmon, compatible id shared-dma-pool
[ 0.000000] cma: Reserved 4 MiB at 0x0000000007c00000
[ 0.145826] ramoops: attached 0x100000@0x7400000, ecc: 0/0

[ 0.000000] OF: reserved mem: initialized node linux,secmon, compatible id shared-dma-pool
[ 0.145826] ramoops: attached 0x100000@0x7400000, ecc: 0/0
这两行log分别为上文写到的,两块在dts中设置的保留的内存

[ 0.000000] Reserved memory: created CMA memory pool at 0x0000000005000000, size 4 MiB
在kernel中查看此行log关键字发现在
kernel/aml-4.9/drivers/base/dma-contiguous.c
==>> static int __init rmem_cma_setup(struct reserved_mem *rmem)
===>> cma_init_reserved_mem(rmem->base, rmem->size, 0, &cma);
* cma_init_reserved_mem() - create custom contiguous area from reserved memory
cma_init_reserved_mem 为使用cma分配一块连续的内存作为保留使用。

然后去网上查,CMA的解释如下,是arm独有的,可以分配一块连续的内存作为预留使用
CMA的全称是contiguous memory allocator, 其工作原理是:预留一段的内存给驱动使用,但当驱动不用的时候,memory allocator(buddy system)可以分配给用户进程用作匿名内存或者页缓存。而当驱动需要使用时,就将进程占用的内存通过回收或者迁移的方式将之前占用的预留内存腾出来, 供驱动使用。

[ 0.000000] cma: Reserved 4 MiB at 0x0000000007c00000
搜索这段log的关键字
kernel/aml-4.9/mm/cma.c
==>> cma_declare_contiguous
==>> cma_init_reserved_mem
最后也指向了cma_init_reserved_mem这个函数,应该是某个内存模块或者驱动调用这个函数,申请预留使用

这里我们就可以计算得出 8M(cma预留内存) + 1M(ramops) + 4M(shared-dma-pool) + 11M(kernel) = 24M

再次分析开机的dmesg log,又发现两块比较有意思的地方
[ 0.000000] modules : 0xffffff8000000000 - 0xffffff8008000000 ( 128 MB)
可以证实我们在dts改动的内存为128Mb的改动生效了

[ 0.000000] Memory: 93200K/133812K available (6588K kernel code, 462K rwdata, 2204K, 1472K init, 498K bss, 32420K reserved, 8192K cma-reserved)
这一行log打印了板子上电视时候的内存分配,这里看vmlinux.o
其中6588K kernel code和1472K init,属于代码段,加一起6588K + 1472K = 8060K,size中test段,8487604 / 1024 = 8288K
462K rwdata 段应该是data段 + rwdata段 2240K + 462K = 2702K,size中data段 2480188 / 1024 = 2422K
498K bss段,size中的bss段 509013 / 1024 = 497K
实际的段大小和size vmlinux这部分要有点差距,这里是因为kernel不光包括vmlinux这一部分还有别的部分共同组成,这里不深究了
这里有一条log为32420K reserved, 8192K cma-reserved引起了我的注意力,找到这条log发现
kernel/aml-4.9/mm/page_alloc.c

    pr_info("Memory: %luK/%luK available (%luK kernel code, %luK rwdata, %luK rodata, %luK init, %luK bss, %luK reserved, %luK cma-reserved ,%luK totalreserve_pages"
        "%s%s)\n",
        nr_free_pages() << (PAGE_SHIFT - 10), 
        physpages << (PAGE_SHIFT - 10), 
        codesize >> 10, datasize >> 10, rosize >> 10,
        (init_data_size + init_code_size) >> 10, bss_size >> 10,
        (physpages - totalram_pages - totalcma_pages) << (PAGE_SHIFT - 10), 
        totalcma_pages << (PAGE_SHIFT - 10), 
        totalreserve_pages << (PAGE_SHIFT - 10), 
        str ? ", " : "", str ? str : "");

32420K是通过剪发算出来的开机启动时候所预留的内存空间,但是进入系统之后有一部分内存被释放吊给用户层使用了
8192K cma-reserved这条log正好符合我们之前计算的两块CMA分配的4M内存一共8M。而totalcma_pages这个变量为申请保留的内存
查看代码得到 cma_init_reserved_mem这个函数中 调用了totalcma_pages += (size / PAGE_SIZE);这一条语句,正如前文所看到的,两块cma的内存申请最终都调用到了这条语句上。

综上:我们可以得知,在linux系统中,可见物理内存小于实际内存,这些内存都用于系统保留以及被kernel占用。
————————————————
版权声明:本文为CSDN博主「wq3028」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wq3028/article/details/81292756

 

8M(cma预留内存) + 1M(ramops) + 4M(shared-dma-pool) + 11M(kernel) = 24M CMA内存虽然在kernel初始化时预留了,但是kernel初始化完成后还是要释放的,会还给buddy system,但还之前会添加MIGRATE_CMA标记,reserved-memory节点下有no-map标记的才会被排除在外,具体可以查看/sys/kernel/debug/memblock/memory文件 kernel在初始化时需要透过memblock动态申请内存,这部分动态申请的加上kernel代码和数据段,还有dtb的空间,差不多就是你这里的24MB了 动态申请的内存主要包括:struct page结构体,Dentry cache,Inode-cache,percpu数据,log buffer等,其中struct page结构体在你的log中是有体现的,这些数据可以查看/sys/kernel/debug/memblock/reserved文件,如果想更详细的调试,可以添加memblock=debug到cmdline中 [ 0.000000] DMA zone: 523 pages used for memmap

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐