导语
linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍 linux 内存组织结构和页面布局,内存碎片产生原因和优化算法,linux 内核几种内存管理的方法,内存使用场景以及内存使用的那些坑。从内存的原理和结构,到内存的算法优化,再到使用场景,去探寻内存管理的机制和奥秘。
一、走进 linux 内存
1、内存是什么?
1) 内存又称主存,是 CPU 能直接寻址的存储空间,由半导体器件制成;
2) 内存的特点是存取速率快,断电一般不保存数据,非持久化设备;
2、内存的作用
1) 暂时存放 cpu 的运算数据
2) 硬盘等外部存储器交换的数据
3) 保障 cpu 计算的稳定性和高性能
二、 linux 内存地址空间
1、linux 内存地址空间 Linux 内存管理全貌
2、内存地址——用户态&内核态
-
用户态:Ring3 运行于用户态的代码则要受到处理器的诸多
-
内核态:Ring0 在处理器的存储保护中,核心态
-
用户态切换到内核态的 3 种方式:系统调用、异常、外设中断
-
区别:每个进程都有完全属于自己的,独立的,不被干扰的内存空间;用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用;内核态线程共享内核地址空间;
3、内存地址——MMU 地址转换
-
MMU 是一种硬件电路,它包含两个部件,一个是分段部件,一个是分页部件
-
分段机制把一个逻辑地址转换为线性地址
-
分页机制把一个线性地址转换为物理地址
4、内存地址——分段机制
1) 段选择符
-
为了方便快速检索段选择符,处理器提供了 6 个分段寄存器来缓存段选择符,它们是:cs,ss,ds,es,fs 和 gs
-
段的基地址(Base Address):在线性地址空间中段的起始地址
-
段的界限(Limit):在虚拟地址空间中,段内可以使用的最大偏移量
2) 分段实现
-
逻辑地址的段寄存器中的值提供段描述符,然后从段描述符中得到段基址和段界限,然后加上逻辑地址的偏移量,就得到了线性地址
5、内存地址——分页机制(32 位)
-
分页机制是在分段机制之后进行的,它进一步将线性地址转换为物理地址
-
10 位页目录,10 位页表项, 12 位页偏移地址
-
单页的大小为 4KB
6、用户态地址空间
-
text:代码段可执行代码、字符串字面值、只读变量
-
data:数据段,映射程序中已经初始化的全局变量
-
bss:存放程序中未初始化的全局变量
-
heap:运行时的堆,在程序运行中使用 malloc 申请的内存区域
-
mmap:共享库及匿名文件的映射区域
-
stack:用户进程栈
7、内核态地址空间
-
直接映射区:线性空间中从 3G 开始最大 896M 的区间,为直接内存映射区
-
动态内存映射区:该区域由内核函数 vmalloc 来分配
-
永久内存映射区:该区域可访问高端内存
-
固定映射区:该区域和 4G 的顶端只有 4k 的隔离带,其每个地址项都服务于特定的用途,如:ACPI_BASE 等
8、进程内存空间
-
用户进程通常情况只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址
-
内核空间是由内核负责映射,不会跟着进程变化;内核空间地址有自己对应的页表,用户进程各自有不同额页表
三、 Linux 内存分配算法
内存管理算法——对讨厌自己管理内存的人来说是天赐的礼物
Linux 内存管理框架
1、内存碎片
1) 基本原理
-
产生原因:内存分配较小,并且分配的这些小的内存生存周期又较长,反复申请后将产生内存碎片的出现
-
优点:提高分配速度,便于内存管理,防止内存泄露
-
缺点:大量的内存碎片会使系统缓慢,内存使用率低,浪费大
2) 如何避免内存碎片
-
少用动态内存分配的函数(尽量使用栈空间)
-
分配内存和释放的内存尽量在同一个函数中
-
尽量一次性申请较大的内存,而不要反复申请小内存
-
尽可能申请大块的 2 的指数幂大小的内存空间
-
外部碎片避免——伙伴系统算法
-
内部碎片避免——slab 算法
-
自己进行内存管理工作,设计内存池
2、伙伴系统算法——组织结构
1) 概念
-
为内核提供了一种用于分配一组连续的页而建立的一种高效的分配策略,并有效的解决了外碎片问题
-
分配的内存区是以页框为基本单位
2) 外部碎片
-
外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域3) 组织结构
-
把所有的空闲页分组为 11 个块链表,每个块链表分别包含大小为 1,2,4,8,16,32,64,128,256,512 和 1024 个连续页框的页块。最大可以申请 1024 个连续页,对应 4MB 大小的连续内存
3、伙伴系统算法——申请和回收
1) 申请算法
-
申请 2^i 个页块存储空间,如果 2^i 对应的块链表有空闲页块,则分配给应用
-
如果没有空闲