Linux内存管理--物理内存分配【转】

简介:
 
 

1. First Fit分配器

    First Fit分配器是最基本的内存分配器,它使用bitmap而不是空闲块列表来表示内存。在bitmap中,如果page对应位为1,则表示此page已经被分配,为0则表示此page没有被分配。为了分配小于一个page的内存块,First Fit分配器记录了最后被分配的PFN (Page Frame Number)和分配的结束地址在页内的偏移量。随后小的内存分配被Merge到一起并存储到同一页中。

   First Fit分配器不会造成严重的内存碎片,但其效率较低,由于内存经常通过线性地址进行search,而First Fit中的小块内存经常在物理内存的开始处,为了分配大块内存而不得不扫描前面大量的内存。

2. Boot Memory分配器

    物理内存分配器如何分配内存来初始化其自己呢?

    答案是:通过Boot Memory分配器来实现,而Boot Memory分配器则通过最基本的First Fit分配器来实现。

2.1 Boot Map定义 

    Boot Map通过数据结构bootmem_data来定义,详见<linux/bootmem.h>,其定义如下所示: 

 

[cpp]  view plain  copy
 
  1. typedef struct bootmem_data {  
  2.   unsigned long node_boot_start; // 描述的物理内存的起始地址  
  3.   unsigned long node_low_pfn;    // 结束物理地址,即ZONE_NORMAL的结束  
  4.   void *node_bootmem_map;        // 描述“使用或空闲的位图”的地址  
  5.   unsigned long last_offset;     // 最后被分配的页内偏移量,即在llast_pos描述的物理页中,  
  6.                                  // 从last_offset开始,没有被分配   
  7.   unsigned long last_pos;        // 最后被分配的页的PFN  
  8. } bootmem_data_t;  

    所有bootmem_data被放于全局变量bdata_list中。

 

2.2 Boot Memory分配器初始化

      每一个CPU架构被要求提供setup_arch函数,它负责获取初始化boot memory分配器的必要参数。不同的CPU架构通过不同的函数来实现,如ARM通过bootmem_init来实现。它负责获取以下参数:

     • min_low_pfn: 系统中可获得的最小的PFN,装载kernel image结束之后的第一页,在mm/bootmem.c中定义

     • max_low_pfn:低端内存(ZONE_NORMAL)中可获得的最大PFN

     • highstart_pfn:高端内存(ZONE_HIGHMEM)的起始PFN

         • highend_pfn:高端内存(ZONE_HIGHMEM)的结束PFN

     • max_pfn:系统中可获得的最大的PFN, 在mm/bootmem.c中定义

     PFN是在物理内存map的偏移量,以page为单位。Kernel可直接访问ZONE_NORMAL,其偏移量为:PAGE_OFFSET。

     通过以上5个参数明确了可用物理内存之后,调用init_bootmem->init_bootmem_core来初始化contig_page_data。它主要完成以下两件事:

     1) 将把与此node对应pgdat_data_t插入到pgdat_list中

     2) 初始化bootmem_data_t的中参数,并分配表示页分配状态的bitmap,其大小为: (end_pfn-start_pfn+7)/8

          bitmap的物理地址为:bootmem_data_t->node_boot_start

          bitmap的虚拟地直为:bootmem_data_t->node_bootmem_map

2.3 分配内存

     • reserve_bootmem:用于预留物理页面。但用于通用的内存分配是低率的,它主要用于各种驱动(如:Video Codec)预留内存。

     常用的内存分配函数如下(in UMA架构,我们常的ARM架构为UMA架构):

     • alloc_bootmem

     • alloc_bootmem_low

     • alloc_bootmem_pages

     • alloc_bootmem_low_pages

     其调用关系如下图所示:

 

 2.3.1  __alloc_bootmem

     __alloc_bootmem() 需要以下参数:

     • pgdat

       用于分配内存块的节点,在UMA架构中,它被忽略,因为它总是为:contig_page_data

     • size

       指定请求分配的内存大小,以字节为单位

     • align

       请求以多少字节对齐,地于小块内存分配,一般以SMP_CACHE_BYTES对齐,如在X86上,与L1硬件cache对齐

     • goal

       偏好的分配内存的起始地址,

2.3.2 __alloc_bootmem_core

     它从goal指定的地址开始,线性地扫描内存,以寻找可以满足内存分配要求的内存块。它的另外一项功能是决定是否需要把新分配的内存块与以前已经分配的内存块merge到一起。

   

      分配内存常用函数定义如下: 

 

[cpp]  view plain  copy
 
  1. #ifdef CONFIG_NO_BOOTMEM  
  2. /* We are using top down, so it is safe to use 0 here */  
  3. #define BOOTMEM_LOW_LIMIT 0  
  4. #else  
  5. #define BOOTMEM_LOW_LIMIT __pa(MAX_DMA_ADDRESS)  
  6. #endif  
  7.   
  8. #define alloc_bootmem(x) \  
  9.     __alloc_bootmem(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)  
  10. #define alloc_bootmem_align(x, align) \  
  11.     __alloc_bootmem(x, align, BOOTMEM_LOW_LIMIT)  
  12. #define alloc_bootmem_nopanic(x) \  
  13.     __alloc_bootmem_nopanic(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)  
  14. #define alloc_bootmem_pages(x) \  
  15.     __alloc_bootmem(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)  
  16. #define alloc_bootmem_pages_nopanic(x) \  
  17.     __alloc_bootmem_nopanic(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)  
  18. #define alloc_bootmem_node(pgdat, x) \  
  19.     __alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)  
  20. #define alloc_bootmem_node_nopanic(pgdat, x) \  
  21.     __alloc_bootmem_node_nopanic(pgdat, x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)  
  22. #define alloc_bootmem_pages_node(pgdat, x) \  
  23.     __alloc_bootmem_node(pgdat, x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)  
  24. #define alloc_bootmem_pages_node_nopanic(pgdat, x) \  
  25.     __alloc_bootmem_node_nopanic(pgdat, x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)  
  26.   
  27. #define alloc_bootmem_low(x) \  
  28.     __alloc_bootmem_low(x, SMP_CACHE_BYTES, 0)  
  29. #define alloc_bootmem_low_pages(x) \  
  30.     __alloc_bootmem_low(x, PAGE_SIZE, 0)  
  31. #define alloc_bootmem_low_pages_node(pgdat, x) \  
  32.     __alloc_bootmem_low_node(pgdat, x, PAGE_SIZE, 0)  

 

2.4 释放内存

     调用free_bootmem来释放内存。

 

[cpp]  view plain  copy
 
  1. void __init free_bootmem(unsigned long addr, unsigned long size)  
  2. {  
  3.     unsigned long start, end;  
  4.   
  5.     kmemleak_free_part(__va(addr), size);  
  6.   
  7.     start = PFN_UP(addr);  
  8.     end = PFN_DOWN(addr + size);  
  9.   
  10.     mark_bootmem(start, end, 0, 0);  
  11. }  























本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/5660218.html,如需转载请自行联系原作者
相关文章
|
13天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
18天前
|
存储 缓存 监控
|
1月前
|
算法 Linux
Linux中内存问题
【10月更文挑战第6天】
41 2
|
16天前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
24 0
|
1月前
|
存储 缓存 固态存储
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
376 0
|
24天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
49 1
|
28天前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
1月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
1月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
41 4