Linux内存管理介绍

linux内存管理概述

内存管理的目标:

提供一种方法,在各种目的各个用户之间实现内存共享,应该实现以下两个功能:

1、最小化管理内存的时间,内存申请和释放响应时间短

2、最优化用于一般应用的可用内存,内存管理(算法)所占用的内存少,浪费的内存少(内存碎片少)

下图为内存分配器的关系:

1、kmalloc用于分配一块以字节数为单位的内存,所分配的内存物理地址是连续的

void *kmalloc(size_t size, gfp_t flags);

size > SLUB_MAX_SIZE(2*PAGE_SIZE),通过buddy system分配,否则通过slab分配器来分配

2、vmalloc分配的内存在虚拟地址上是连续的,而物理地址无需连续

void *vmalloc(unsigned long size);

3、buddy system是Linux内存管理的核心部分。用户态内存是通过缺页处理由buddy system分配,然后再由用户态的内存管理算法进行管理

内存管理概述图

这张图中描述了包含很多的信息,如果全部都了解了内存管理也就大概了解了。

buddy system系统

外碎片:频繁地请求和释放不同大小的一组连续页框,必然导致在已分配页框的块内分散了许多小块的空闲页框。由此带来的问题是,即使有足够多的空闲页框可以可以满足请求,但要分配一个大块的连续页框就可能无法满足。

伙伴系统算法就是要解决内存管理中著名的外碎片问题。

buddy system把页框(一般4k)作为基本单位,对物理内存进行管理。其思想是以2的幂为基准来合并或者拆分连续的页。幂(以order表示)的范围一般从0到10,代表了1,2,4,8,16,32,64,128,256,512,1024个连续的页框。

根据order的不同,空闲页框由11个链表来管理,例如order 2的链表,其每个节点表示4个连续的页框。

当用户申请1个页时:

1、buddy system会先搜索order 0的链表,看看是否为空

a)如果不会空,则将1个节点(1页)返回给用户

b)如果链表为空,则搜索order 1的链表

2、buddy system 搜索order 1的链表,看看是否为空

a)如果链表不为空,则将1个order(2页)的节点拆分成2个order 0的节点,然后将其中一个返回给用户

b)如果链表为空,则搜索order 2的链表

释放内存的过程是分配的逆过程。buddy system算法试图把大小为size的一对空闲节点合并为大小2*size的节点

slab分配器

内碎片:内碎片产生的主要原因是由于请求内存的大小与分配给它的大小不匹配造成的

采用伙伴算法分配内存时,每次至少分配一个页面。但当请求分配的内存大小为几十个字节或几百个字节时应该如何处理?如何在一个页面中分配小的内存区,小内存区的分配所产生的内碎片又如何解决?Linux采用Slab

这里先给出slab分配器的一些特性,下面的文字可能晦涩难懂,大家可以先把后面的数据结构和相关算法掌握了再回过头来浏览:

(1)所存放数据的类型可以影响内存区的分配方式。例如,当给用户态进程分配一个页框时,内核调用get_zeroed_page()函数用0 填充这个页。slab 分配器概念扩充了这种思想,并把内存区看作对象(object),这些对象由一组数据结构和几个叫做构造(constructor)或析构(destructor)的函数(或方法)组成。前者初始化内存区,而后者回收内存区。为了避免重复初始化对象,slab分配器并不丢弃已分配的对象,而是释放但把它们保存在内存中。于是,slab分配器具有了想当一部分缓存的功能,当以后又要请求新的对象时,就可以从内存获取而不用重新初始化。

(2)内核函数倾向于反复请求同一类型的内存区。例如,只要内核创建一个新进程,它就要为一些固定大小的数据结构分配内存区。当进程结束时,包含这些数据结构的内存区还可以被重新使用。因为进程的创建和撤消非常频繁,在没有slab分配器时,内核把时间浪费在反复分配和回收那些包含同一内存区的页框上;slab分配器把那些页框保存在高速缓存中并很快地重新使用它们。

(3)对内存区的请求可以根据它们发生的频率来分类。对于预期频繁请求一个特定大小的内存区而言,可以通过创建一组具有适当大小的专用对象来高效地处理,由此以避免内碎片的产生。另一种情况,对于很少遇到的内存区大小,可以通过基于一系列几何分布大小(如早期Linux 版本所使用的2的幂次方大小)的对象的分配模式来处理,即使这种方法会导致内碎片的产生(即普通缓存与专用缓存)。

(4)在引入的对象大小不是几何分布的情况下,也就是说,数据结构的起始地址不是物理地址值的2 的幂次方,事情反倒好办。这可以借助处理器硬件高速缓存而导致较好的性能。

(5)硬件高速缓存的高性能又是尽可能地限制对伙伴系统分配器调用的另一个理由,因为对伙伴系统函数的每次调用都“弄脏”硬件高速缓存,所以增加了对内存的平均访问时间。内核函数对硬件高速缓存的影响就是所谓的函数“足迹(footprint)”,其定义为函数结束时重写高速缓存的百分比。显而易见,大的“足迹”导致内核函数刚执行之后较慢的代码执行,因为硬件高速缓存此时填满了无用的信息。

slab 分配器把对象分组放进高速缓存。每个高速缓存都是同种类型对象的一种“储备”。例如,当一个文件被打开时,存放相应“打开文件”对象所需的内存区是从一个叫做filp(“文件指针”)的slab 分配器的高速缓存中得到的。

包含高速缓存的主内存区被划分为多个slab,每个slab 由一个或多个连续的页框组成,这些页框中既包含已分配的对象,也包含空闲的对象。我们将在以后有关回收页框的博文中看到,内核周期性地扫描高速缓存并释放空slab 对应的页框。

slab 分配器的主要结构

每个缓存都包含了一个 slabs 列表,这是一段连续的内存块(通常都是页面)。存在 3 种 slab:

slabs_full    完全分配的 slab

slabs_partial 部分分配的 slab

slabs_empty   空 slab,或者没有对象被分配

注意:slabs_empty 列表中的 slab 是进行回收(reaping)的主要备选对象。正是通过此过程,slab 所使用的内存被返回给操作系统供其他用户使用。

slab 列表中的每个 slab 都是一个连续的内存块(一个或多个连续页),它们被划分成一个个对象。这些对象是从特定缓存中进行分配和释放的基本元素。注意 slab 是 slab 分配器进行操作的最小分配单位,因此如果需要对 slab 进行扩展,这也就是所扩展的最小值。通常来说,每个 slab 被分配为多个对象。

由于对象是从 slab 中进行分配和释放的,因此单个 slab 可以在 slab 列表之间进行移动。例如,当一个 slab 中的所有对象都被使用完时,就从 slabs_partial 列表中移动到 slabs_full 列表中。当一个 slab 完全被分配并且有对象被释放后,就从 slabs_full 列表中移动到 slabs_partial 列表中。当所有对象都被释放之后,就从 slabs_partial
列表移动到 slabs_empty 列表中。

数据结构全图如下所示

普通和专用高速缓存

高速缓存被分为两种类型:普通和专用。普通高速缓存只有slab分配器用于自己的目的,而专用高速缓存由内核的其余部分使用。

普通高速缓存:

内存区大小的范围一般包括13个集合分布的内存区。一个叫做malloc_sizes的表(其元素类型为cache_sizes)分别指向26个高速缓存描述符,与其相关的内存区大小为32, 64, 128, 256, 512,1024, 2048, 4096, 8192, 16384, 32768, 65536
和131072 字节。对于每种大小,都有两个高速缓存:一个适用于ISA DMA 分配,另一个适用于常规分配。在系统初始化期间调用kmem_cache_init()来建立普通高速缓存

专用高速缓存是由kmem_cache_create()函数创建。

Linux内存信息查看

/proc/meminfo      查看系统整体的内存信息

/proc/buddyinfo    查看空闲物理页的信息

/proc/pagetypeinfo 查看不同类型的空闲物理页信息

/proc/slabinfo     查看slab分配器的所有cache信息

/sys/kernel/slab/  目录下是slab分配器的各个cache的具体信息

时间: 2024-08-10 19:03:00

Linux内存管理介绍的相关文章

Linux内存管理 【转】

转自:http://blog.chinaunix.net/uid-25909619-id-4491368.html Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用.在本章最后,我们给出一个内存映射的实例,帮助网友们理解内核内存管理与用户内存管理之间的关系,希望大家最终能驾驭Linux内存管理. 前言 内存管理一向是所有操作系统书

linux内存管理---物理地址、线性地址、虚拟地址、逻辑地址之间的转换

linux内存管理---虚拟地址.逻辑地址.线性地址.物理地址的区别(一) 这篇文章中介绍了四个名词的概念,下面针对四个地址的转换进行分析 CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步(如下图): 首先,将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!!!),CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址, 其次,再利用其页式内存管理单元,转换为最终物理地址. 这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程.之所以这样冗余

Linux内存管理1

1.前言 关于内存管理的系列文章主要是对陈莉君老师所讲述的内存管理知识的整理. 本文将主要以X86架构来介绍Linux内存管理的相关知识. 2. 内存寻址 内存寻址是操作系统设计的硬件基础之一 操作系统是横跨软件和硬件的桥梁 操作系统设计者必须在硬件相关代码和硬件无关代码之间划分清晰的界限,以便操作系统很容易的移植到不同的平台 内存寻址的不同时期 (1)石器时代---8位寻址:4004是4位寻址,8080是8位寻址,由一个主累加器(寄存器A)和6个次累加器(寄存器B,C,D,E,H和L),没有段

linux内存管理---虚拟地址、逻辑地址、线性地址、物理地址的区别(一)

分析linux内存管理机制,离不了上述几个概念,在介绍上述几个概念之前,先从<深入理解linux内核>这本书中摘抄几段关于上述名词的解释: 一.<深入理解linux内核>的解释 逻辑地址(Logical Address) 包含在机器语言指令中用来指定一个操作数或一条指令的地址(有点深奥).这种寻址方式在80x86著名的分段结构中表现得尤为具体,它促使windows程序员把程序分成若干段.每个逻辑地址都由一个段和偏移量组成,偏移量指明了从段开始的地方到实际地址之间的距离. 线性地址(

关于linux内存管理

 Linux的内存管理主要分为两部分:物理地址到虚拟地址的映射,内核内存分配管理(主要基于slab). 物理地址到虚拟地址之间的映射 1.概念 物理地址(physical address) 用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相相应.--这个概念应该是这几个概念中最好理解的一个,可是值得一提的是,尽管能够直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,可是其实,这仅仅是一个硬件提供给软件的抽像,

伙伴系统之伙伴系统概述--Linux内存管理(十四)

日期 内核版本 架构 作者 GitHub CSDN 2016-09-02 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 1 前景回顾 1.1 Linux内存管理的层次结构 Linux把物理内存划分为三个层次来管理 层次 描述 存储节点(Node) CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 管理区(Zone)

Linux内存管理3---分页机制

1.前言 本文所述关于内存管理的系列文章主要是对陈莉君老师所讲述的内存管理知识讲座的整理. 本讲座主要分三个主题展开对内存管理进行讲解:内存管理的硬件基础.虚拟地址空间的管理.物理地址空间的管理. 本文将主要以X86架构为例来介绍Linux内存管理的分页机制. 2.分页机制 5. Linux中的汇编 6.Linux系统地址映射示例

转 Linux内存管理原理

Linux内存管理原理 在用户态,内核态逻辑地址专指下文说的线性偏移前的地址Linux内核虚拟3.伙伴算法和slab分配器 16个页面RAM因为最大连续内存大小为16个页面 页面最多16个页面,所以16/2order(0)bimap有8个bit位两个页框page1 与page2组成与两个页框page3 与page4组成,这两个块之间有一个bit位 order(1)bimap有4个bit位order(2)bimap有4个bit位的2个页面分配过程 当我们需要order(1)的空闲页面块时,orde

Linux内存管理2---段机制

1.前言 本文所述关于内存管理的系列文章主要是对陈莉君老师所讲述的内存管理知识讲座的整理. 本讲座主要分三个主题展开对内存管理进行讲解:内存管理的硬件基础.虚拟地址空间的管理.物理地址空间的管理. 本文将主要以X86架构为例来介绍Linux内存管理的段机制. 2.段机制 段是虚拟地址空间的基本单位 段机制必须把虚拟地址空间的一个地址转换为线性地址空间的一个线性地址 可以从三个方面来描述段(如下图) 段的基地址(Base):在线性地址空间中段的起始地址: 段的界限(Limit):在虚拟地址空间中,