您的位置首页>企业动态>

Linux kernel内存管理模块结构分析

导读 大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Linux kernel内存管理模块结构分析问题。1 介绍在Linux系统中,内存是一种涉及面很

大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Linux kernel内存管理模块结构分析问题。

1.介绍

在Linux系统中,内存是一种涉及面很广的资源,从应用程序到内核和驱动程序。再加上其天然的稀缺性,内存管理是linux内核中一个非常重要和复杂的子系统。

重要性不多说了。仁有自己的分寸。复杂性(鉴于Linux内核出色的抽象能力)不应该被普通人(Linux系统的用户、应用工程师、驱动工程师和轻量级内核工程师)感知。事实上,内核掩盖了大部分实现细节,并试图以简单易用的方式向其他模块提供内存服务。

但是这个世界上没有十全十美,内核的内存管理也是如此,原因有二:一是大家很难调整,内存管理相关的要求太复杂;第二,CPU、Device、Memory之间纠结的三角恋(参考下图)导致它也(不得不)提供了很多繁琐且不可理解的功能(困扰了很多从入门级到高级的linux软件工程师)。

1图片中央处理器、设备和内存

基于以上原因,本网站内存管理子系统发表了很多分析文章,帮助人们理解内存管理的相关概念。然而,到目前为止,仍然缺乏一篇索引文章。从整体来看,可以了解到Kernel内存管理需要面对的软硬件情况,需要解决的问题,以及各个内存管理子模块的功能和意义。这就是本文的目的。

2.内存相关要求概述

在嵌入式系统中,从需求角度看内存非常简单,可以归纳为两类(参考上图1):

1)CPU需要访问内存,包括从内存中取指、从内存中取数据、将数据写入内存。相应的数据流是:

可选内存

ext-indent: 2em;">2)其它外部设备有访问内存的需求,包括从内存“读取”数据、向内存“写入”数据。这里的“读取”和“写入”加了引号,是因为在大部分情况下,设备不像CPU那样有智能,不具备直接访问内存的能力。总结来说,设备有三种途径访问内存:

a)由CPU从中中转,数据流如下(本质上是CPU访问内存): DeviceCPU<--------------------Memory

b)由第三方具有智能的模块(如DMA控制器)中转,数据流如下: DeviceDMA ControllerMemory

c)直接访问内存,数据流如下: DeviceIOMMU(Optional)--------->Memory

那么Linux kernel的内存管理模块怎么理解并满足上面的需求呢?接下来我们将一一梳理。

3. 软件(Linux kernel内存管理模块)的角度看内存3.1 CPU视角

我们先从CPU的需求说起(以当前具有MMU功能的嵌入式Linux平台为例),看看会向kernel的内存管理模块提出哪些需求。

⬛ 看到内存

关于内存以及内存管理,最初始的需求是:Linux kernel的核心代码(主要包括启动和内存管理),要能看到物理内存。

在MMU使能之前,该需求很容易满足。

但MMU使能之后、Kernel的内存管理机制ready之前,Kernel看到的是虚拟地址,此时需要一些简单且有效的机制,建立虚拟内存到物理内存的映射(可以是部分的映射,但要能够满足kenrel此时的需要)。

⬛ 管理内存

看到内存之后,下一步就是将它们管理起来。根据不同的内存形态(在物理地址上是否连续、是否具有NUMA内存、是否具有可拔插的内存、等等),可能有不同的管理模型和管理方法。

⬛ 向内核线程/用户进程提供服务

将内存管理起来之后,就可以向其它人(kernel的其它模块、内核线程、用户空间进程、等等)提供服务了,主要包括: ✓ 以虚拟地址(VA)的形式,为应用程序提供远大于物理内存的虚拟地址空间(Virtual Address Space) ✓ 每个进程都有独立的虚拟地址空间,不会相互影响,进而可提供非常好的内存保护(memory protecTIon) ✓ 提供内存映射(Memory Mapping)机制,以便把物理内存、I/O空间、Kernel Image、文件等对象映射到相应进程的地址空间中,方便进程的访问 ✓ 提供公平、高效的物理内存分配(Physical Memory AllocaTIon)算法 ✓ 提供进程间内存共享的方法(以虚拟内存的形式),也称作Shared Virtual Memory ✓ 等等

⬛ 更为高级的内存管理需求

欲望是无止境的,在内存管理模块提供了基础的内存服务之后,Linux系统(包括kernel线程和用户进程)已经可以正常work了,更为高级的需求也产生了,例如: ✓ 内存的热拔插(memory hotplug) ✓ 内存的size超过了虚拟地址可寻址的空间怎么办(high memory) ✓ 超大页(hugetlbpage)的支持 ✓ 利用磁盘作为交换页以扩大可用内存(各种swap机制和算法) ✓ 在NUMA系统中通过移动物理页面位置的方法提升内存的访问效率(Page migraTIon) ✓ 内存泄漏的检查 ✓ 内存碎片的整理 ✓ 内存不足时的处理(oom kill机制) ✓ 等等

3.2 Device视角

正常情况下,当软件活动只需要CPU参与时(例如简单的数学运算、图像处理等),上面3.1 所涉及内容已经足够了,无论是用户空间程序,还是内核空间程序,都可以欢快的运行了。

不过,当软件操作一些特殊的、可以以自己的方式访问memory的硬件设备的时候,麻烦就出现了:软件通过CPU视角获得memory,并不能直接被这些硬件设备访问。于是这些硬件设备就提出了需求:

内存管理模块需要为这些设备提供一些特殊的获取内存的接口,这些接口可以按照设备所期望的形式组织内存(因而可以被设备访问),也可以重新映射成CPU视角的形式,以便CPU可以访问。

这就是我们在编写设备驱动的时候会经常遇到的DMA mapping功能,其中DMA是Direct Memory Access的所需,表示(memory)可以被设备直接访问。

另外,在某些应用场景下,内存数据可能会在多个设备间流动,为了提高效率,不能为每个设备都提供一份拷贝,因此内存管理模块需要提供设备间内存共享(以及相互转换)的功能。

4. 软件结构

基于上面章节的需求,Linux kernel从虚拟内存(VM)、DMA mapping以及DMA buffer sharing三个角度,对内存进行管理,如下图所示:

其中VM是内存管理的主要模块,也是我们通常意义上所讲的狭义“内存管理”,代码主要分布在mm/以及arch/xxx/mm/两个目录下,其中arch/xxx/mm/*提供平台相关部分的实现,mm/*提供平台无关部分的实现。

DMA mapping是内存管理的辅助模块,注要提供dma_alloc_xxx(申请可供设备直接访问的内存----dma_addr)和dma_map_xxx(是在CPU视角的虚拟内存和dma_addr之间转换)两类接口。该模块的具体实现依赖于设备访问内存的方式,代码主要分别在drivers/base/*(通用实现)以及arch/xxx/mm/(平台相关的实现)。

最后是DMA buffer sharing的机制,用于在不同设备之间共享内存,一般包括两种方法:

传统的、利用CPU虚拟地址中转的方法,例如scatterlist;

dma buffer sharing framework,位于drivers/dma-buf/dma-buf.c中。      

有关这些模块的具体描述,本文后续的文章将会一一展开(有些已经有了),这里就先结束吧.

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。