乐趣区

关于内存管理:Linux内核内存管理总结

微信公众号:奔跑吧 linux 社区

加奔跑吧微信群请先加微信:runninglinuxkernel
欢送订阅奔跑吧配套旗舰篇视频节目:https://weidian.com/?userid=1…

这篇对《奔跑吧 Linux 内核》第一版的内存治理 做了很棒的笔记和总结。《奔跑吧 Linux 内核》第一版各大书店有售。
这篇文章作者是:Arnold Lu@南京
原文:https://www.cnblogs.com/arnol…

Linux 的内存治理波及到的内容十分庞杂,而且与内核的方方面面耦合在一起,想要了解透彻十分艰难。
在开始学习之前进行了一些筹备工作《如何开展 Linux Memory Management 学习?》,

  1. 参考资料
    遂决定以如下材料作为参考,进行 Linux 内存治理的钻研:

《奔跑吧 Linux 内核》:以第 2 章为底本开展,这是目前能获取的紧跟以后内核倒退(Linux 4.0),并且讲的比拟全面的一本材料。

《Understanding the Linux Virtual Memory Manager》:简略说就是虽老但经典,基于(Linux 2.4/2.6)。作者是目前依然沉闷在 Linux 社区 MM 专家。

《wowotech Memory Management》:没有其余系列经典,也没有条理系列的介绍 MM,然而依然值得按考。

《tolimit Linux 内存源码剖析》:绝对零散的介绍了内存相干剖析文档

《Linux Kernel v4.0》:当然必不可少的,是源码了。

当逐步深刻看到 MMU 相干代码时,读一下 ARM 架构对于 MMU 的规格书有助于了解。

不然对于虚拟地址到物理地址的映射就会很虚无,这些材料包含《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》的《Virtual Memory System Architecture》,以及相干 MMU TRM。

  1. Linux Memory Management 框架图
    整个内存治理从宏观上能够分为三大部分:用户空间、内核空间和相干硬件。

用户空间次要是 libc 对相干零碎调用进行封装,对用户程序提供 API,罕用的有 malloc、mmap、munmap、remap、madvise、mempolicy 等等。

相干硬件包含 MMU/TLB、L1/L2 Cache 以及 DDR RAM,具体到 ARM 架构须要对照 MMU/L2 Cache 以及 RAM 规格书。

内核空间就简单多了,首先介绍初始化及初始化后的布局。

2.1 物理内存初始化从获取内存大小、初始化页表,再进行 zone 初始化,而后在 zone 中应用搭档零碎进行物理内存初始化;

2.2 页表的映射过程讲述了 ARM32 和 ARM64 两种架构下的页表映射,如何从虚拟地址由 MMU 转化成物理页面地址的;

2.3 内核内存的布局图在内存被初始化之后,内核的内存布局基本上就确定了,ARM32 和 ARM64 下布局有很大区别。在 malloc 一节 brk 中介绍了用户空间的布局。
2.1~2.3 是内存的一个动态状态,在有了这些根底之后,2.4~2.9 依照从低层到下层的一一介绍了。
2.4 调配物理页面介绍了基于搭档零碎的页调配和开释;
2.5 slab 分配器基于搭档零碎,slab 调配更小内存块;以及基于 slab 的 kmalloc;
2.6 vmalloc 和 kmalloc 区别在于 v,即在 VMALLOC 区域调配;
2.7 VMA 即 Virtual Memory Area,是过程内存治理的外围;
2.8 malloc 和 2.9 mmap 都基于 VMA,malloc/free 用于调配 / 开释一块内存;mmap/munmap 用于匿名 / 文件映射到用户空间。以及 mmap(补充)。
因为 malloc/mmap 分配内存并不是立刻调配,只是在用到的时候才会触发 2.10 缺页中断解决。
在缺页但页有余的状况下,就须要进行一些操作调整内存,这些操作的根底是 2.11 page 援用计数,还有页面的 2.12 反向映射 RMAP 技术。
在内存不足状况下触发 kswapd2.13 回收页面,其中匿名页面有着非凡的 2.14 匿名页面生命周期。
在 kswapd 回收仍然无奈满足内存调配,就须要对内存进行 2.16 内存规整,它依赖的技术是 2.15 页面迁徙。
因为内存中存在一些内容齐全一样的页面,应用 2.17 KSM 技术进行合并,同时利用 COW 技术,在须要时重新分配。
还介绍了 2.18 Dirty COW 内存破绽,而后对内存治理数据结构和 API 进行了总结 2.19 总结内存治理数据结构和 API。
最初 2.20 最新更新和瞻望对新技术进行了介绍。
除了以上技术,还有如下内存技术:

swap 计数把匿名页面写入 SWAP 分区从而开释出闲暇页面

内存压缩技术 zram(a compressed RAM based swap device)

zswap 技术是 zram 和 swap 的一个综合,首先将待换出页面进行压缩,存储到零碎 RAM 动态分配的内存池中;达到肯定阈值后再写入理论替换设施。

在内存极其有余状况下应用 21 OOM(Out-Of-Memory)来杀死不重要过程获取更多内存的技术

基于 cgroup 的 Memory 资源管制

解决多媒体对大量间断内存需要的 CMA(Contiguous Memory Allocator)技术

slub 分配器

memory hotplug 内存热插拔反对动静更换内存物理设施

在对内存相干技术理解过后,就是如何使用的问题了?
一方面是对内存问题进行定位;另一方面是对内存行为施加影响,进行优化。
22 内存检测技术对 Linux 内存常见问题及其定位办法和工具 (slub_debug/kmemleak/kasan) 进行了解说。
23 一个内存 Oops 解析以一个内存 Oops 为例,介绍了内存相干异样剖析。
内存 sysfs 节点和工具介绍了 linux 内存治理相干 sysfs 节点,以及工具;借助这些能够对内存进行优化。
扩大浏览:
对于 zram、zswap、zcache 的区别与各自优缺点《zram vs zswap vs zcache Ultimate guide: when to use which one》

Linux 内存治理框架图

  1. 代码和测试环境搭建
    3.1 QEMU
    装置 QEMU 以及相干编译工具

sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential
3.2 Busybox 1.24
下载 Busybox 1.24 代码:

git clone https://github.com/arnoldlu/b… -b 1_24_stable
编译 Busybox:

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-

make menuconfig #P684,进行配置

make -j4 install
配置 initramfs:

sudo cp -r running_kernel_initramfs/* _install/
sudo chmod +x _install/etc/init.d/rcS
cd _install
mkdir mnt
mkdir dev
cd dev
sudo mknod console c 5 1
sudo mknod null c 1 3
3.3 Kernel 4.0
下载 Linux Kernel 4.0 代码:

git clone https://github.com/arnoldlu/l… -b running_kernel_4.0
编译 Linux Kernel:

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-
make vexpress_defconfig #P685 进行配置
make bzImage -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
make dtbs
3.4 运行内核

Run Kernel+Busybox in QEMU

qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append “rdinit=/linuxrc console=ttyAMA0 loglevel=8” -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic
至此,曾经有一个残缺的环境,提供 shell 命令。

  1. 思考问答
    在系统启动时,ARM Linux 内核如何晓得零碎中有多大的内存空间?

在 32bit Linux 内核中,用户空间和内核空间的比例通常是 3:1,能够批改成 2:2 吗?

物理内存页面如何增加到搭档零碎中,是一页一页增加,还是以 2 的几次幂来退出呢?

内核的一级页表寄存在什么中央?二级页表又寄存在什么中央?

用户过程的一级页表寄存在什么中央?二级页表呢?

在 ARM32 零碎中,页表是如何映射的?在 ARM64 零碎中,页表又是如何映射的?

请简述 Linux 内核在现实状况下页面分配器 (page allocator) 是如何调配出间断物理页面的。

在页面分配器中,如何从调配掩码 (gfp_mask) 中确定能够从哪些 zone 中分配内存?

页面分配器是依照什么方向来扫描 zone 的?

为用户过程调配物理内存,调配掩码应该选用 GFP_KERNEL,还是 GFP_HIGHUSER_MOVABLE 呢?

slab 分配器是如何调配和开释小块内存的?

slab 分配器中有一个着色的概念(cache color),着色有什么作用?

slab 调配其中的 slab 对象有没有依据 Per-CPU 做一些优化?

slab 增长并导致大量不必的闲暇对象,该如何解决?

请问 kmalloc、vmalloc 和 malloc 之间有什么区别以及实现上的差别?

应用用户态的 API 函数 malloc()分配内存时,会马上为其调配物理内存吗?

假如不思考 libc 的因素,malloc 调配 100Byte,那么实际上内核是为其调配 100Byte 吗?

假如两个用户过程打印的 malloc()调配的虚拟地址是一样的,那么在内核中这两块虚拟内存是否打架了呢?

vm_normal_page()函数返回的是什么样页面的 struct page 数据结构?为什么内存治理代码中须要这个函数?

请简述 get_user_page()函数的作用和实现流程?

请简述 follow_page()函数的作用和实现流程?

请简述公有映射和共享映射的区别。

为什么第二次调用 mmap 时,Linux 内核没有捕捉到地址重叠并返回失败呢?

struct page 数据结构中的_count 和_mapcount 有什么区别?

匿名页面和 page cache 页面有什么区别?

struct page 数据结构中有一个锁,请问 trylock_page()和 lock_page()有什么区别?

在 Linux 2.4.x 内核中,如何从一个 page 找到所有映射该页面的 VMA?反应映射能够带来哪些便当?

浏览 Linux 4.0 内核 RMAP 机制的代码,画出父子过程之间 VMA、AVC、anon_vma 和 page 等数据结构之间的关系图。

在 Linux 2.6.34 中,RMAP 机制采纳了新的实现,在 Linux 2.6.33 和之前的版本中称为旧版本 RMAP 机制。那么在旧版本 RMAP 机制中,如果父过程有 1000 个子过程,每个子过程都有一个 VMA,这个 VMA 外面有 1000 个匿名页面,当所有的子过程的 VMA 同时产生写复制时会是什么状况呢?
当 page 退出 lru 链表中,被其余线程开释了这个 page,那么 lru 链表如何晓得这个 page 曾经被开释了。

kswapd 内核线程何时会被唤醒?

LRU 链表如何晓得 page 的流动频繁水平?

kswapd 依照什么准则来换出页面?

kswapd 依照什么方向来扫描 zone?

kswapd 以什么规范来退出扫描 LRU?

手持设施例如 Android 零碎,没有 swap 分区或者 swap 文件,kswapd 会扫描匿名页面 LRU 吗?

swappiness 的含意是什么?kswapd 如何计算匿名页面和 page cache 之间的扫描比重?

当零碎充斥着大量只拜访一次的文件拜访 (use-one streaming IO) 时,kswapd 如何来躲避这种风暴?

在回收 page cache 时,对于 dirty 的 page cache,kswapd 会马上回写吗?

内核有哪些页面会被 kswapd 写回替换分区?

ARM32 Linux 如何模仿这个 Linux 版本的 L_PTE_YOUNG 比特位呢?

如何了解 Refault Distance 算法?

请简述匿名页面的生命周期。在什么状况下会产生匿名页面?在什么条件下会开释匿名页面?

KSM 是基于什么原理来合并页面的?

在 KSM 机制里,合并过程中把 page 设置成写爱护的函数 write_protect_page()有这样一个判断:这个判断的根据是什么?

如果多个 VMA 的虚构页面同时映射了同一个匿名页面,那么此时 page->index 应该等于多少?

为什么 Dirty COW 小程序能够批改一个只读文件的内容?

在 Dirty COW 内存破绽中,如果 Diryt COW 程序没有 madviseThread 线程,即只有 procselfmemThread 线程,是否批改 foo 文件的内容呢?

假如在内核空间获取了某个文件对应的 page cache 页面的 struct page 数据结构,而对应的 VMA 属性是只读,那么内核空间是否能够胜利批改该文件呢?

如果用户过程应用只读属性 (PROT_READ) 来 mmap 映射一个文件到用户空间,而后应用 memcpy 来写这段内存空间,会是什么样的状况?

请画出内存治理中罕用的数据结构的关系图,如 mm_struct、vma、vaddr、page、pfn、pte、zone、paddr 和 pg_data 等,并思考如下转换关系。

请画出在最蹩脚的状况下调配若干个间断物理页面的流程图。

在 Android 中新增加了 LMK(Low Memory Killer),请形容 LMK 和 OOM Killer 之间的关系。

请形容一致性 DMA 映射 dma_alloc_coherent()函数在 AEM 中是如何治理 cache 一致性的?

请形容流式 DMA 映射 dma_map_single()函数在 ARM 中是如何治理 cache 一致性的?

为什么在 Linux 4.8 内核中要把基于 zone 的 LRU 链表机制迁徙到基于 Node 呢?

退出移动版