收藏 分享(赏)

e820与内存探测.docx

上传人:hwpkd79526 文档编号:7132725 上传时间:2019-05-06 格式:DOCX 页数:9 大小:28.32KB
下载 相关 举报
e820与内存探测.docx_第1页
第1页 / 共9页
e820与内存探测.docx_第2页
第2页 / 共9页
e820与内存探测.docx_第3页
第3页 / 共9页
e820与内存探测.docx_第4页
第4页 / 共9页
e820与内存探测.docx_第5页
第5页 / 共9页
点击查看更多>>
资源描述

1、e820 与 kernel 物理内存映射deltamaster posted 大约 1 年前 in kernel with tags linux kernel RAM , 1435 阅读我们都对操作系统如何管理内存有一定的了解,然而,在操作系统开始管理内存之前,首先要获取物理内存的信息,比如一共有多少物理地址是可用的,有哪些物理地址是被ACPI(Advanced Configuration and Power Interface)数据使用,这些信息从何而来呢?e820 就是 BIOS 像 x86 架构(包括 x86_64)上的操作系统引导程序提供物理内存信息的功能。当请求 BIOS 中断号 1

2、5H,并且置操作码 AX=E820H 的时候, BIOS 就会向调用者报告可用的物理地址区间等信息,e820 由此得名。Linux 内核也通过这种机制来获得物理地址信息,使用 dmesg 可以看到相关的信息:?123456789101112131415161718192021222324252627282930 0.000000 BIOS-provided physical RAM map: 0.000000 BIOS-e820: 0000000000000000 - 000000000009e800 (usable) 0.000000 BIOS-e820: 000000000009e800

3、- 00000000000a0000 (reserved) 0.000000 BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved) 0.000000 BIOS-e820: 0000000000100000 - 0000000020000000 (usable) #511MB 0.000000 BIOS-e820: 0000000020000000 - 0000000020200000 (reserved) 0.000000 BIOS-e820: 0000000020200000 - 0000000040000000 (usable)

4、 #510MB 0.000000 BIOS-e820: 0000000040000000 - 0000000040200000 (reserved) 0.000000 BIOS-e820: 0000000040200000 - 00000000aac0d000 (usable) #1706MB 0.000000 BIOS-e820: 00000000aac0d000 - 00000000aad8e000 (reserved) 0.000000 BIOS-e820: 00000000aad8e000 - 00000000aad95000 (usable) 0.000000 BIOS-e820:

5、00000000aad95000 - 00000000aad96000 (reserved) 0.000000 BIOS-e820: 00000000aad96000 - 00000000aad97000 (usable) 0.000000 BIOS-e820: 00000000aad97000 - 00000000aadb8000 (reserved) 0.000000 BIOS-e820: 00000000aadb8000 - 00000000aadc6000 (usable) 0.000000 BIOS-e820: 00000000aadc6000 - 00000000aade8000

6、(reserved) 0.000000 BIOS-e820: 00000000aade8000 - 00000000aaf23000 (usable) 0.000000 BIOS-e820: 00000000aaf23000 - 00000000aafe8000 (ACPI NVS) 0.000000 BIOS-e820: 00000000aafe8000 - 00000000aaffd000 (usable) 0.000000 BIOS-e820: 00000000aaffd000 - 00000000ab000000 (ACPI data) 0.000000 BIOS-e820: 0000

7、0000ab000000 - 00000000b0000000 (reserved) 0.000000 BIOS-e820: 00000000e0000000 - 00000000e4000000 (reserved) 0.000000 BIOS-e820: 00000000fec00000 - 00000000fec01000 (reserved) 0.000000 BIOS-e820: 00000000fed10000 - 00000000fed14000 (reserved) 0.000000 BIOS-e820: 00000000fed18000 - 00000000fed1a000

8、(reserved) 0.000000 BIOS-e820: 00000000fed1c000 - 00000000fed20000 (reserved) 0.000000 BIOS-e820: 00000000fee00000 - 00000000fee01000 (reserved) 0.000000 BIOS-e820: 00000000ff980000 - 00000000ffc00000 (reserved) 0.000000 BIOS-e820: 00000000ffd80000 - 0000000100000000 (reserved) 0.000000 BIOS-e820: 0

9、000000100000000 - 000000014f800000 (usable) #1272MB上面是我在自己计算机上得到的数据,其中 usable 的区间就是实际被映射到物理内存上的地址空间,上面标注出来的四个区间,就是我主要的四个可用的物理地址区间了,大约4GB。Usable:已经被映射到物理内存的物理地址。Reserved:这些区间是没有被映射到任何地方,不能当作 RAM 来使用,但是 kernel 可以决定将这些区间映射到其他地方,比如 PCI 设备。通过检查/proc/iomem 这个虚拟文件,就可以知道这些 reserved 的空间,是如何进一步分配给不同的设备来使用了。AC

10、PI data:映射到用来存放 ACPI 数据的 RAM 空间,操作系统应该将 ACPI Table 读入到这个区间内。ACPI NVS:映射到用来存放 ACPI 数据的非易失性存储空间,操作系统不能使用。Unusable:表示检测到发生错误的物理内存。这个在上面例子里没有,因为比较少见。内核读到这些信息后,将其保存在 e820map 结构体中,有两个副本,一个符号名叫e820,还有一个符号名叫 e820_saved。具体的数据结构可以参考arch/x86/include/asm/e820.h。随着内核的启动,内核还会修改 e820 的信息。?123 0.000000 e820 update

11、range: 0000000000000000 - 0000000000010000 (usable) = (reserved) 0.000000 e820 remove range: 00000000000a0000 - 0000000000100000 (usable) 0.000000 e820 update range: 00000000ab000000 - 0000000100000000 (usable) = (reserved)比如在我的系统上发生了这样三次 e820 的改动,这些改动已经与 BIOS 没有任何关系了,只是内核自己通过修改自己的内存数据结构,来改变自己对内存区间的

12、使用。还有一个典型的例子是当内核启动参数中有置顶 memmap 这样的参数项时,内核会修改指定的 usable 的区间为 reserved,这样内核就不能将虚拟地址映射到这些物理地址空间了,换言之就是不能使用这块物理内存了(其实通过 ioremap 还是可以将其映射到内核的虚拟地址空间的,但是这些行为都是自己控制的而不会受到其他程序的干扰) 。这样当我们需要自己管理某段物理内存而不希望内核干预时就很有用处,比如基于物理内存的文件系统,就可以这样实现。内核中还提供了一系列操作 e820 数据结构的函数,函数声明都在arch/x86/include/asm/e820.h,相应的定义都在 arch/

13、x86/kernel/e820.c 中。文章开头看到的这段信息,就是由 void _init e820_print_map(char *who)来打印的。在这个过程中,e820_saved 始终保持原始的状态不变,以便查询 BIOS 提供的真实映射信息,不过内核目前好像没有使用这个数据结构。我曾经有使用过它,用来检查某个物理地址是否确实是物理内存。inux 在被 bootloader 加载到内存后, cpu 最初执行的 linux 内核代码是/header.S 文件中的 start_of_setup 函数,这个函数在做了一些准备工作后会跳转到 boot 目下文件 main.c的 main 函数

14、执行,在这个 main 函数中我们可以第一次看到与内存管理相关的代码,这段代码调用 detect_memeory()函数检测系统物理内存在 header.S 中执行下面汇编代码:1. start_of_setup: 2. . 3. # Jump to C code (should not return) 4. calll main 5. . 跳到 boot 目录下的 main.c 文件中1. void main(void) 2. 3. 4. /* Detect memory layout */ 5. detect_memory();/*内存探测函数*/ 6. 7. 1. int detect_

15、memory(void) 2. 3. int err = -1; 4. 5. if (detect_memory_e820() 0) 6. err = 0; 7. 8. if (!detect_memory_e801() 9. err = 0; 10. 11. if (!detect_memory_88() 12. err = 0; 13. 14. return err; 15. 由上面的代码可知,linux 内核会分别尝试调用 detect_memory_e820()、detcct_memory_e801()、detect_memory_88()获得系统物理内存布局,这 3 个函数内部其实都

16、会以内联汇编的形式调用 bios 中断以取得内存信息,该中断调用形式为 int 0x15,同时调用前分别把 AX 寄存器设置为 0xe820h、0xe801h、0x88h,关于 0x15 号中断有兴趣的可以去查询相关手册。下面分析 detect_memory_e820()的代码,其它代码基本一样。1. #define SMAP 0x534d4150 /* ASCII “SMAP“ */ 2. /*由于历史原因,一些 i/o 设备也会占据一部分内存 3. 物理地址空间,因此系统可以使用的物理内存空 4. 间是不连续的,系统内存被分成了很多段,每个段 5. 的属性也是不一样的。int 0x15 查

17、询物理内存时每次 6. 返回一个内存段的信息,因此要想返回系统中所有 7. 的物理内存,我们必须以迭代的方式去查询。 8. detect_memory_e820()函数把 int 0x15 放到一个 do-while 循环里, 9. 每次得到的一个内存段放到 struct e820entry 里,而 10. struct e820entry 的结构正是 e820 返回结果的结构!而像 11. 其它启动时获得的结果一样,最终都会被放到 12. boot_params 里,e820 被放到了 boot_params.e820_map。 13. */ 14. static int detect_me

18、mory_e820(void) 15. 16. int count = 0;/*用于记录已检测到的物理内存数目 */ 17. struct biosregs ireg, oreg; 18. struct e820entry *desc = boot_params.e820_map; 19. static struct e820entry buf; /* static so it is zeroed */ 20. 21. initregs(/*初始化 ireg 中的相关寄存器*/ 22. ireg.ax = 0xe820; 23. ireg.cx = sizeof buf;/*e820entry

19、 数据结构大小*/ 24. ireg.edx = SMAP;/*标识 */ 25. ireg.di = (size_t)/*int15 返回值的存放处*/ 26. 27. /* 28. * Note: at least one BIOS is known which assumes that the 29. * buffer pointed to by one e820 call is the same one as 30. * the previous call, and only changes modified fields. Therefore, 31. * we use a temp

20、orary buffer and copy the results entry by entry. 32. * 33. * This routine deliberately does not try to account for 34. * ACPI 3+ extended attributes. This is because there are 35. * BIOSes in the field which report zero for the valid bit for 36. * all ranges, and we dont currently make any use of t

21、he 37. * other attribute bits. Revisit this if we see the extended 38. * attribute bits deployed in a meaningful way in the future. 39. */ 40. 41. do 42. /*在执行这条内联汇编语句时输入的参数有: 43. eax 寄存器=0xe820 44. dx 寄存器=SMAP 45. edi 寄存器=desc 46. ebx 寄存器=next 47. ecx 寄存器=size 48. 49. 返回给 c 语言代码的参数有: 50. id=eax 寄存器

22、 51. rr=edx 寄存器 52. ext=ebx 寄存器 53. size=ecx 寄存器 54. desc 指向的内存地址在执行 0x15 中断调用时被设置 55. */ 56. intcall(0x15, 57. /*选择下一个*/ 58. ireg.ebx = oreg.ebx; /* for next iteration. */ 59. 60. /* BIOSes which terminate the chain with CF = 1 as opposed 61. to %ebx = 0 dont always report the SMAP signature on 62.

23、 the final, failing, probe. */ 63. if (oreg.eflags 65. 66. /* Some BIOSes stop returning SMAP in the middle of 67. the search loop. We dont know exactly how the BIOS 68. screwed up the map at that point, we might have a 69. partial map, the full map, or complete garbage, so 70. just return failure.

24、*/ 71. if (oreg.eax != SMAP) 72. count = 0; 73. break; 74. 75. 76. *desc+ = buf;/*将 buf 赋值给 desc*/ 77. count+;/*探测数加一*/ 78. 79. while (ireg.ebx 7. /*调用 x86 体系下的 memory_setup 函数*/ 8. who = x86_init.resources.memory_setup(); 9. /*保存到 e820_saved 中*/ 10. memcpy( 11. printk(KERN_INFO “BIOS-provided physi

25、cal RAM map:n“); 12. /*打印输出 */ 13. e820_print_map(who); 14. 在 x86_init.c 中定义了 x86 下的 memory_setup 函数1. struct x86_init_ops x86_init _initdata = 2. 3. .resources = 4. 5. .memory_setup = default_machine_specific_memory_setup, 6. , 7. 8. ; 1. char *_init default_machine_specific_memory_setup(void) 2. 3

26、. char *who = “BIOS-e820“; 4. u32 new_nr; 5. /* 6. * Try to copy the BIOS-supplied E820-map. 7. * 8. * Otherwise fake a memory map; one section from 0k-640k, 9. * the next section from 1mb-appropriate_mem_k 10. */ 11. new_nr = boot_params.e820_entries; 12. /*将重叠的去除 */ 13. sanitize_e820_map(boot_para

27、ms.e820_map, 14. ARRAY_SIZE(boot_params.e820_map), 15. 16. /*去掉重叠的部分后得到的内存个数*/ 17. boot_params.e820_entries = new_nr; 18. /*将其赋值到全局变量 e820 中,小于 0 时,为出错处理*/ 19. if (append_e820_map(boot_params.e820_map, boot_params.e820_entries) 20. addr; 5. u64 size = biosmap-size; 6. u64 end = start + size; 7. u32

28、type = biosmap-type; 8. /* Overflow in 64 bits? Ignore the memory map. */ 9. if (start end) 10. return -1; 11. /*添加函数*/ 12. e820_add_region(start, size, type); 13. biosmap+; 14. nr_map-; 15. 16. return 0; 17. 1. void _init e820_add_region(u64 start, u64 size, int type) 2. 3. _e820_add_region( 4. e82

29、0 为 e820map 结构1. struct e820map 2. _u32 nr_map; 3. struct e820entry mapE820_X_MAX; 4. ; 其中 E820_X_MAX 大小为 128.1. tatic void _init _e820_add_region(struct e820map *e820x, u64 start, u64 size, 2. int type) 3. 4. int x = e820x-nr_map; 5. 6. if (x = ARRAY_SIZE(e820x-map) 7. printk(KERN_ERR “Ooops! Too many entries in the memory map!n“); 8. return; 9. 到这里,物理内存就已经从 BIOS 中读出来存放到全局变量 e820 中,e820 是 linux 内核中用于建立内存管理框架的基础。在后面我们会看到,建立初始化节点、管理区会用到他。见 http:/

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报