ToB企服应用市场:ToB评测及商务社交产业平台
标题:
Linux内存管理(Linux内存架构,malloc,slab的实现)
[打印本页]
作者:
忿忿的泥巴坨
时间:
昨天 08:16
标题:
Linux内存管理(Linux内存架构,malloc,slab的实现)
媒介
本篇文章开始解说Linux的内存管理,深入相识内存管理有助于我们深入Linux底层逻辑,加强对Linux的学习和相识。
一、Linux进程空间内存分配
下面是一个32位的Linux进程空间内存排布具体的环境:
内核空间(1GB):内核空间是操作系统核心代码和数据的所在地区。这部分空间由操作系统内核独占,用于实行操作系统的使命,如操作系统的调理、内存管理、驱动步调和系统调用等。在内核空间中,全部的内核模块和驱动步调都运行,并可以直接访问硬件资源。
用户空间(3GB):用户空间是给用户步调运行的地区,其中包罗了应用步调的代码、数据和堆栈等。用户空间是被操作系统管理和分配的,用户步调在这个空间中运行,并通过系统调用与内核举行交互。用户空间中的进程无法直接访问内核空间中的数据和功能,而必须通过系统调用接口来哀求内核的服务。
在用户空间中,可以进一步细分差别的段:
1.代码段:存储可实行步调的指令,即步调的二进制代码。
2.数据段:存储全局变量和静态变量等数据。
3.堆区:用于动态分配内存,通过调用malloc()、new等函数实现。
4.栈区:用于存储函数调用时的局部变量和函数调用的上下文。
这种地点空间的分配方式可以提供对操作系统和应用步调的良好隔离,确保系统的稳定性和安全性。操作系统内核在内核空间中运行,可以对硬件资源举行直接访问和管理,而用户步调在用户空间中运行,通过系统调用接口与内核举行通信,实现各种操作和功能。
二、malloc的实现机理
当使用malloc函数时会调用到brk系统调用。
brk系统调用用于改变进程的数据段的竣事地点,从而调解进程的堆空间大小。当调用malloc函数时,它会向操作系统哀求一块指定大小的内存空间。操作系统会查抄当前的堆空间大小,并根据必要调解堆的竣事地点,以供新的内存分配。
malloc函数会调用brk系统调用来增加堆空间的大小,以顺应所需分配的内存大小。如果哀求的内存大小超过了当前堆空间的剩余大小,则操作系统会扩展堆空间,使其满足哀求。反之,如果哀求的内存大小较小,当前剩余的堆空间大概会被重新分割,并返回相应大小的内存块给malloc函数。
调用brk系统调用将会调用到下面的函数:
这个函数的主要作用是根据用户空间的哀求调解进程的数据段竣事地点,从而动态调解进程的堆空间大小。
SYSCALL_DEFINE1(brk, unsigned long, brk)
{
unsigned long retval;
unsigned long newbrk, oldbrk;
struct mm_struct *mm = current->mm;
struct vm_area_struct *next;
unsigned long min_brk;
bool populate;
if (down_write_killable(&mm->mmap_sem))
return -EINTR;
#ifdef CONFIG_COMPAT_BRK
/*
* CONFIG_COMPAT_BRK can still be overridden by setting
* randomize_va_space to 2, which will still cause mm->start_brk
* to be arbitrarily shifted
*/
if (current->brk_randomized)
min_brk = mm->start_brk;
else
min_brk = mm->end_data;
#else
min_brk = mm->start_brk;
#endif
if (brk < min_brk)
goto out;
/*
* Check against rlimit here. If this check is done later after the test
* of oldbrk with newbrk then it can escape the test and let the data
* segment grow beyond its set limit the in case where the limit is
* not page aligned -Ram Gupta
*/
if (check_data_rlimit(rlimit(RLIMIT_DATA), brk, mm->start_brk,
mm->end_data, mm->start_data))
goto out;
newbrk = PAGE_ALIGN(brk);
oldbrk = PAGE_ALIGN(mm->brk);
if (oldbrk == newbrk)
goto set_brk;
/* Always allow shrinking brk. */
if (brk <= mm->brk) {
if (!do_munmap(mm, newbrk, oldbrk-newbrk))
goto set_brk;
goto out;
}
/* Check against existing mmap mappings. */
next = find_vma(mm, oldbrk);
if (next && newbrk + PAGE_SIZE > vm_start_gap(next))
goto out;
/* Ok, looks good - let it rip. */
if (do_brk(oldbrk, newbrk-oldbrk) < 0)
goto out;
set_brk:
mm->brk = brk;
populate = newbrk > oldbrk && (mm->def_flags & VM_LOCKED) != 0;
up_write(&mm->mmap_sem);
if (populate)
mm_populate(oldbrk, newbrk - oldbrk);
return brk;
out:
retval = mm->brk;
up_write(&mm->mmap_sem);
return retval;
}
复制代码
三、物理内存与假造内存
1.物理内存
物理内存是指计算机中现实存在的硬件内存。它是由RAM(Random Access Memory,随机存取内存)或其他类似的硬件组成的,用于存储正在实行的步调和数据。物理内存是直接与计算机的中央处理器(CPU)相连,提供快速、随机访问数据的能力。
2.假造内存
假造内存是操作系统提供的一种抽象概念。它扩展了可用的内存空间,使得步调可以使用比物理内存更大的地点空间。假造内存将步调所需的内存分为连续的地点空间块,称为假造地点空间。每个步调都有自己的假造地点空间,这使得每个步调能够以独立的方式实行,而不会互相干扰。
假造内存的工作原理是将部分步调和数据存储在物理内存中,而将不常用的部分保存在磁盘上的交换文件中。当步调必要访问假造内存中的某个地点时,操作系统会根据某种映射机制将假造地点转换为物理地点。这个过程被称为假造内存管理。通过这种方式,系统可以运行更多的步调,即使物理内存有限。
假造内存的主要优势之一是提供了更大的地点空间,以支持大型步调和多使命操作系统。它还可以通过将不常用的数据存储在磁盘上来节省物理内存的使用,并提供更好的内存管理和数据保护机制。
总结而言,物理内存是计算机现实的硬件内存,而假造内存是通过操作系统提供的抽象层面扩展的内存概念,使得步调能够使用比物理内存更大的地点空间,并提供更好的内存管理和保护机制。
四、磁盘和物理内存区别
物理内存和磁盘是差别的存储介质,它们在计算机系统中扮演差别的脚色和功能。物理内存用于临时存储当前正在实行的步调和数据,而磁盘用于长期存储文件和持久性数据。
五、页
在 Linux 操作系统中,“页”(Page)是内存管理中的一个告急概念,尤其是在假造内存管理中。操作系统通过页来管理物理内存和假造内存之间的映射,使得每个进程能够独立地使用其假造地点空间,而不直接操作物理内存。这种分页管理有助于实现内存保护、内存共享、内存映射文件等机制。
页的根本概念:
假造内存与物理内存
:
假造内存
:是操作系统为每个进程提供的假造地点空间。每个进程看到的内存地点是连续的,这与现实的物理内存地点无关。
物理内存
:是计算机硬件中的现实内存(RAM),其中存储了系统运行时的步调数据和操作系统的内核。
页的根本界说
:
页(Page)
:假造内存的根本单位,操作系统将假造地点空间分别为大小雷同的块,每个块叫做“页”。通常,一页的大小为 4KB(也可以更大,如 2MB 或 1GB),具体大小取决于体系结构(例如,x86 体系结构通常为 4KB)。
页帧(Page Frame)
:物理内存的根本单位,物理内存被分别为大小雷同的块,每个块叫做“页帧”,对应假造内存中的“页”。
页表(Page Table)
:
页表是操作系统用于将假造地点转换为物理地点的结构。它存储了假造页到物理页帧的映射信息。每个进程都有自己的页表,操作系统通过页表来实现假造内存到物理内存的映射。
页表的结构通常是多级的(如 2 级页表、4 级页表等),以提高映射效率并减少内存斲丧。
分页管理的核心概念:
假造地点空间分别
:
假造内存被分别成多个大小相当的页。每个进程的假造内存都由操作系统分别成多少个假造页,而每个假造页与物理内存中的页帧通过页表建立映射。
页面映射
:
在一个分页系统中,每个假造地点空间的页都与物理内存的页帧相对应。操作系统使用页表来记录这种映射关系。
当进程访问假造内存时,操作系统会通过查找页表来获取对应的物理内存地点。
如果假造页没有映射到物理页帧(即页面不在内存中),操作系统会触发缺页异常(Page Fault),并将数据从磁盘加载到内存。
页替换
:
由于物理内存的有限性,大概会发生内存不足的环境。这时,操作系统必要将某些不常用的假造页面交换到磁盘上的交换空间(Swap Space)中,将内存中的页面替换出来以腾出空间。当必要这些被替换的页面时,它们会重新加载回内存。
大页(Huge Pages)
:
对于某些必要大量内存的应用步调(如数据库、高性能计算等),操作系统允许使用大页(如 2MB 或 1GB 页),以减少页表的开销。大页可以减少页表项的数目,提高内存访问效率。
页对齐
:
在假造内存系统中,页是按照肯定的对齐方式来管理的,通常是 4KB。页对齐确保了假造地点空间和物理内存的页边界一致性。
页面保护与权限
:
每个页都有肯定的访问权限,这些权限决定了该页面的访问行为:
读权限
(Read)
写权限
(Write)
实行权限
(Execute)
这些权限通过页表项设置,并在进程访问该页面时由硬件控制。
Linux 中分页的实现:
在 Linux 操作系统中,假造内存管理采用了分页机制。具体来说,Linux 中有以下几个关键组件与分页管理密切相关:
内存管理单元(MMU)
:
MMU 是硬件部分,它负责实行假造地点到物理地点的转换。MMU 使用页表来举行地点转换,确保假造内存和物理内存的隔离。
页表(Page Table)
:
在 Linux 中,页表是用来维护假造地点到物理地点映射的结构。每个进程有自己的页表,操作系统会维护内核的页表。页表结构通常是多级的,包罗顶级页表、二级页表等。
进程假造地点空间
:
每个进程拥有自己的假造地点空间,Linux 会将进程的假造内存分别为多个区段,如代码段、数据段、堆、栈等。每个区段的假造内存都有对应的页表条目。
缺页异常处理(Page Fault Handling)
:
当进程访问未映射的假造内存时,MMU 会触发缺页异常。Linux 内核会处理缺页异常,判断是通过磁盘交换加载数据照旧分配新的物理页面来办理问题。
交换空间(Swap Space)
:
当物理内存不足时,Linux 会使用交换空间(swap)将一些不常用的页存储到磁盘中,从而腾出内存空间。交换空间的存在允许系统使用更多的内存,只管这会降低性能。
总结:
页
是内存管理的根本单位,操作系统将假造内存分成多个大小雷同的页,并通过页表映射到物理内存的页帧。
分页
可以有效地隔离进程的假造内存,提供内存保护、共享、以及懒加载等机制。
Linux
使用分页机制来管理假造内存,硬件的 MMU 和操作系统的页表配合工作来完成假造地点到物理地点的转换。
分页和假造内存技能使得操作系统能够更高效、安全地管理内存,提高了系统的可伸缩性和稳定性。
六、搭档算法
搭档算法(Buddy Allocation System)是一种内存分配算法,主要用于动态内存管理,特殊是在操作系统中用来管理物理内存的分配和开释。它通过将内存块分成具有2的幂大小的地区,并通过将相邻的空闲块配对来实现高效的内存分配。
搭档算法的核心概念:
内存分配单位
:
搭档算法将内存分别为大小为2的幂的块(例如 2KB、4KB、8KB 等)。每个内存块的大小是2的幂,因此,内存块的大小总是是 2, 4, 8, 16, 32, 64 … 等。
分区
:
内存被分别为多个固定大小的块(例如 4KB、8KB、16KB),这些块可以根据必要举行分配。当哀求内存时,算法会从大块开始寻找能够满足哀求的内存块。
搭档
:
内存中的每一块内存有一个“搭档”,这个搭档是与该块内存相邻且大小雷同的块。如果一个块的内存被分配,另一个块(它的搭档)会保持空闲状态,直到其搭档被开释。
合并与分裂
:
当一个内存块被哀求时,搭档算法会根据哀求的大小找到最小的、得当的空闲块。如果所需的内存块比当前空闲块大,则必要分割一个更大的块,并将其分成两个“搭档”块。这个过程会一直递归下去,直到找到合适的块为止。
当开释内存时,算法会查抄是否有空闲块的搭档,如果有,则将它们合并成一个更大的块。这个合并过程会递归举行,直到没有更多的搭档可以合并为止。
搭档算法的工作原理:
分配内存
:
当步调哀求分配大小为 size 的内存时,搭档算法会在空闲内存块中寻找足够大的块。
如果没有一个空闲块的大小与 size 准确匹配,则从更大的块开始分配,直到找到一个合适的块。对于大于哀求的空闲块,系统会将其分裂成两个雷同大小的搭档,继续举行分配。
开释内存
:
当步调开释一个内存块时,系统会查抄它的搭档是否也空闲。如果搭档是空闲的,两个搭档块就会合并成一个更大的块。
合并后的内存块会继续与它的搭档合并,直到没有更多的搭档可以合并为止。合并过程是递归举行的,大概会发生多次合并。
搭档算法的优缺点:
长处:
快速分配和开释
:
由于内存块的大小是2的幂,因此可以通过简朴的计算快速找到合适的内存块。这使得内存分配和开释的速率比较快。
制止外部碎片
:
由于内存块大小的限制(2的幂次),即使内存中出现了一些“空闲”空间,也能够通过合并操作将碎片整理成较大的块,减少外部碎片问题。
内存合并
:
搭档算法能够在开释内存时举行合并操作,将相邻的空闲内存块合并成一个更大的内存块,从而有效地使用了内存资源。
缺点:
内部碎片
:
虽然搭档算法可以制止外部碎片,但由于内存块大小是2的幂次,大概会出现内部碎片问题。即一个块比现实必要的内存大,导致浪费一部分内存。
例如,如果哀求的内存大小为 6KB,但最小可分配的块为 8KB,那么分配的内存将比现实必要的多出 2KB。
固定块大小
:
由于内存块的大小是2的幂次,无法处理一些非常小的内存哀求(例如 1KB 或 3KB),大概导致某些内存的浪费。
管理复杂度
:
搭档算法的合并操作必要肯定的管理工作,尤其是在大量的内存块必要管理时。每次分配和开释内存时都必要更新搭档关系。
搭档算法的实现:
在实现搭档算法时,通常使用一个位图(Bitmap)或者链表来管理每个内存块的空闲和占用状态。常见的实现方式如下:
位图管理
:
使用一个位图来记录每个内存块的状态,每个位表示一个内存块的状态(空闲或已分配)。对于每个内存块,还会记录它的搭档的位置。
链表管理
:
将每个大小雷同的内存块放入一个链表中,并通过合并操作将它们毗连在一起。每个内存块都可以存储它的搭档信息。
分配策略
:
当哀求内存时,算法会从最大的块开始查找,直到找到能够满足哀求的最小块。如果找到了更大的块,则将它分割为两个雷同大小的搭档,直到获得合适的块。
合并策略
:
在开释内存时,算法会查抄开释的块是否和它的搭档是相邻的,如果是相邻的并且空闲,则将这两个块合并成一个更大的块,并将新的块插入到链表中。
例子:
假设内存的最小分配块为 16 字节,并且内存总量为 64 字节。在内存管理开始时,内存将被分别为多个 16 字节的块,形成一个空闲块链表。假设用户哀求了 24 字节的内存:
由于哀求是 24 字节,而最小块是 16 字节,搭档算法将选择一个 32 字节的块举行分配。
如果哀求的内存已经分配出去,系统会将剩余的 8 字节(32 - 24)与相邻的搭档块举行合并,形成一个 16 字节的块。
总结:
搭档算法是一个高效的内存分配和开释算法,通过将内存块分成 2 的幂大小的块,并通过合并相邻的空闲块来减少外部碎片。只管它存在肯定的内部碎片和管理复杂度,但在许多操作系统和内存管理系统中,它仍然是一种常用的内存分配策略。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4