当前位置: 首页 > Linux

内存管理——物理内存

时间:2023-04-06 22:26:42 Linux

这篇文章是从我自己的角度来写我对物理内存管理的理解。由于Linux引入了虚拟内存的概念,应用程序对物理内存的访问由内核模块接管。于是,带着下面的疑问,相关的细节逐渐揭开:内核访问物理内存使用什么地址?为什么物理内存需要分区?伙伴系统和SLAB系统有什么区别?页框管理如果要管理内存,首先要知道有哪些内存可用,并记录内存状态。默认情况下,物理内存以4k为单位进行划分,每个单位称为一个页框。内核使用结构页数组跟踪内存中每个页框的当前状态。数组的每个元素对应物理内存中的一个页框,数组定义如下://`structpage`定义在`linux/mm_types.h`structpage*mem_map;比如mem_map[0]包含了一个pageframe的第一个Information名词描述:pageframe:存储数据的内存块Page:pageframe中存储的数据块是这样的,内核通过索引所有的内存使用pageframe数组,知道每个页面的情况,例如:是否免费,谁是所有者。为什么要分区?但是,只有分页对于内核来说是不够的,内核没有办法完全直接访问内存。是什么原因?具体还是要说说内存分配过程。进程在申请内存时,会调用malloc()、mmap()等内存分配函数,最终会发起系统调用落入内核态进行内存分配。但是,内存分配过程只分配虚拟地址空间,并没有将相应的物理内存分配给虚拟内存。当进程访问没有建立映射关系的虚拟内存时,会触发缺页中断。当一个进程被pagefault中断后,进程会再次进入内核态,寻找/分配一个pageframe,建立映射关系(虚拟地址到物理地址)。可以看到进程在分配时两次进入内核态。然而Twice却完全不同。要理解这一点,首先要熟悉“进程上下文”与“中断上下文”这两个概念。表示运行在内核空间的进程。(2)内核态,运行在中断上下文中,内核代表运行在内核空间的硬件。(3)用户态,运行在用户空间。内核的地址空间不仅要支持硬件访问,还需要映射到进程的虚拟地址空间,成为进程上下文的一部分。当然,单从实现的角度来说,对于(1)和(2)两种情况,如果内核上下文和进程上下文完全分离也是可行的,而且更简单。但从性能的角度来看,目前的解决方案更好。详见:《User Space on Top of Kernel Space Versus Separated Address Spaces》分区地址映射在32位系统中,内核模块的地址空间只有1G。但是,内核必须访问所有4G内存。但是,内核对物理内存的访问不同于进程对虚拟内存的访问。虚实映射既消耗空间又消耗性能(详见:地址映射),并且在内核场景下,对内存移动和内存交换的需求不高,没有多进程隔离的需要(详见:memorysharingfordetails),映射的好处并不大。因此,内核将页框分组到不同的区域(ZONE)。内核空间的前896MB(不仅是内核代码,还有它的数据)被“直接”映射到物理内存。最后128MB的虚拟内核空间被映射到物理“高端内存”的某个部分(>896MB)。物理内存的直接映射允许物理页面分配器直接访问获得的页面而不需要任何映射操作。获取物理页面的虚拟地址所需的唯一操作是添加一个固定的偏移量。通过以上方法,既实现了4G内存的访问,又保证了内核访问的性能。最终,物理内存的页框组织成如下形式。从内核地址空间的虚实转换来看,是这样的:内存分配器对空闲内存的分配和管理,交给了内存分配器。内核中有两种类型的内存分配器,伙伴系统分配器和SLAB分配器。前者是页框分配器,后者是对象分配器。伙伴系统的引入为内核提供了一种有效的分配策略来分配一组连续的页面。避免频繁申请和释放不同大小的连续页框,导致许多小的空闲页框散落在已分配页框的内存块中,而其他需要分配连续页框的请求得不到满足。SLAB作用于频繁分配和释放的对象,例如进程描述符和内核中其他常见的小对象。如果直接使用伙伴系统进行分配和释放,不仅会造成大量的内部碎片,而且处理速度也会太慢。SLAB分配器是基于对象进行管理的。同一类型的对象归为一类(如进程描述符)。每当要申请这样一个对象时,从一个SLAB链表中分配同样大小的内存,当要释放时,将其存回链表中。buddy系统解决内存外部碎片问题,而SLAB解决内存内部碎片问题。所谓外部碎片是指由于频繁申请和释放页框而导致的一些小的连续页框,而内部碎片是指分配了但不能利用的内存。两个系统的细节暂时压下,以后再详细讨论。本文作者:cyningsun本文地址:https://www.cyningsun.com/06-...版权声明:除特别声明外,本博客所有文章均采用CCBY-NC-ND3.0CN许可协议.转载请注明出处!