linux 内核内存机制之e820(linux启动时,利用e820读取物理内存信息)

    前言:

 我们在进入linux 内存管理学习的时候,我们首先要知道,操作系统首先要在启动的时候把目前机器上有多少内存要了解清楚,内存是怎么布局的,是被怎么用的。否则你学习内存管理就成了无本之源。操作系统初始化自身所需的最重要的关键信息之一就是机器上可用 物理内存的映射。从根本上说,操作系统获取该信息的最佳方式是使用 BIOS。由于我们经常关注的是大型服务器,内存很大,但是往往嵌入式设备或单片机等领域内存是很小的,反而提升了解决问题的复杂性。所以内核会很多版本,甚至兼容性处理等。

     下面分析基于内核的日志文件的输出,内核输出日志信息就包括了内核在启动时,是按照一定的次序在进行检测、配置等。 

  [root@aozhejin]$dmesg >kernel.log  #保存文件来分析

   
     一、linux启动和bios的E820
         这里注意,前提你机器采用的是传统的BIOS模式启动,这里不讨论UEFI或其他类启动模式。

      1、wiki的解释如下: 
           https://en.wikipedia.org/wiki/E820     

    1.1 e820是基于x86的计算机系统的bios 向操作系统或引导加载程序报告内存映射的工具的简写。

    linux 内核通过调用 bios的 int 15h 中断来访问,方法是将EAX 寄存器设置为十六进制值 E820(eax=E820)它会报告哪些内存地址范围可用,哪些保留供 BIOS 使用。 BIOS-e820 通常是引导linux内核首先报告的内容,也可以使用dmesg命令查看。
         1.2  BIOS 功能:INT 0x15,EAX = 0xE820
    到目前为止,检测 PC 内存的最佳方法是调用BIOS的 INT 0x15, EAX = 0xE820 命令。此功能适用于 2002 年以来制造的所有 PC,以及在那之前的大多数现有 PC。它是唯一可以检测 4G 以上内存区域的 BIOS 功能。它旨在成为最终的内存检测 BIOS 功能。
         你可以查看 这里 看到更多的的bois int 0x15 eax=...的功能

      2、内核源码中的注解
         E:\linux内核\E:\linux内核\linux-3.12.37\arch\x86\kernel\e820.c 

e820 is shorthand to refer to the facility by which the BIOS of x86-based computer systems 
It is accessed via the int 15h call, by setting the AX register to value E820 in hexadecimal. 
It reports which memory address ranges are usable and which are reserved for use by the BIOS.
翻译过来和上面是一样的
The e820_saved is directly saved after the BIOS-provided memory map is copied
e820_saved结构体会保存bios提供内存映射

      3、内核启动日志中bios提供的物理ram映射信息(dmesg命令查看或查看/var/log/dmesg)
            会分两步
      第一步: 内核调用中断,读取bios检测到的物理ram映射地址,下面就是读取后处理的输出

/*********************************************************************************************
//
start_kernel 函数定义在:E:\linux内核\linux-3.12.37\init\main.c(汇编启动.S调用) //setup_arch 函数定义在:E:\linux内核\linux-3.12.37\arch\x86\kernel\setup.c //setup_memory_map 函数定义在: E:\linux内核\linux-3.12.37\arch\x86\kernel\e820.c 内核函数调用链 start_kernel ----------->setup_arch ------------------>setup_memory_map 函数打印输出 **************************************************************************************************/ //bios提供的物理ram映射 [ 0.000000] e820: BIOS-provided physical RAM map: [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009ebff] usable 634kb [ 0.000000] BIOS-e820: [mem 0x000000000009ec00-0x000000000009ffff] reserved 4kb [ 0.000000] BIOS-e820: [mem 0x00000000000dc000-0x00000000000fffff] reserved 143kb [ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x00000000bfecffff] usable 3069MB [ 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 1g [ 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-0x000000043fffffff] usable

计算内存地址大小方法1
0x00000000bfecffff-0x0000000000100000=BFDCFFFF (转成十进制) =3218931711(字节)/1024/1024=3069MB
计算内存地址大小方法2
方法2: 输入linux命令,下面得到的是MB大小
[root@aozhejin /]#echo $((0x0000000100000000/1024/1024))

   上面我们的内容18GiB内存的服务器,其“可用”内存介于4GiB(0x100000000)和~18GiB(0x43fffffff)之间:
 说明:

Usable(可用):     已经被映射到物理内存的物理地址。
Reserved(保留): 这些区间是没有被映射到任何地方,不能当作RAM来使用,但是kernel可以决定将这些区间映射到其他地方,
                    比如PCI设备。通过检查/proc/iomem这个虚拟文件,就可以知道这些reserved的空间,是如何进一步分配给不同的设备来使用了。
ACPI data(ACPI 可回收内存):映射到用来存放ACPI数据的RAM空间,操作系统应该将ACPI Table读入到这个区间内。
ACPI NVS(ACPI NVS 内存):映射到用来存放ACPI数据的非易失性存储空间,操作系统不能使用。
Unusable(不可用):表示检测到发生错误的物理内存。这个在上面例子里没有,因为比较少见。

e820.c 在处理内存映射的时候,把内存分了几类:
1.usable (内核保留的)
2.reserved
3.ACPI data
4.ACPI NVS
5.unusable

    第二步: (第一步之后还会利用 drivers/firmware/dmi_scan.c 进行SMBIOS检测工作)
      内核读到这些信息后,将其保存在e820map结构体中。

linux-3.12.37\arch\x86\kernel\e820.c: 源码如下:
struct e820map e820;
struct e820map e820_saved;
e820 和e820_saved都是e820map别名
1) e820 结构体可以被修改
2)e820_saved 结构体保存bios中读取到的物理内存映射信息

   第三步、继续

[    0.000000] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
[    0.000000] e820: remove [mem 0x000a0000-0x000fffff] usable
[    0.000000] e820: last_pfn = 0x440000 max_arch_pfn = 0x400000000

 

   参考资料:
     https://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map
    https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt
     https://www.kernel.org/doc/html/v5.6/x86/boot.html 

posted @ 2023-03-15 19:39  jinzi  阅读(1289)  评论(0编辑  收藏  举报