Linux中内存管理详解

x33g5p2x  于2021-09-26 转载在 Linux  
字(2.0k)|赞(0)|评价(0)|浏览(544)

内存管理简介

内存管理是一个系统的重要组成部分。程序的存放和执行都离不开内存。如何高效的对内存进行分配和回收是非常重要的。

虚拟内存和地址映射

如果用户进程直接操作物理地址会有以下的坏处:
1、 用户进程可以直接操作内核对应的内存,破坏内核运行。
2、 用户进程也会破坏其他进程的运行

通过引入逻辑地址,每个进程都拥有单独的逻辑地址范围。

当进程申请内存的时候,会为其分配逻辑地址和物理地址,并将逻辑地址和物理地址做一个映射

不同进程的逻辑地址可以一样,但是其映射的物理地址是不一样的。

逻辑地址对应的为虚拟内存,物理地址对应的是物理内存。

用户和内核 直接面对的都是虚拟内存。虚拟内存并不是一个真正的存储器,而是一些地址的集合。

CPU中寄存器中存储的是逻辑地址,需要进行映射才能转化为对应的物理地址,然后获取对应的内存。

虚拟内存 分为 内核空间用户空间

内核空间 就是 操作系统内核使用的空间,对应的物理内存用来存放内核代码内核数据等。

用户空间 就是 用户进程使用的空间,对应的物理内存用来存放进程对应代码和数据。

内存映射详解

1、32位映射详解

物理内存被分为三块:

1、ZONE_DMA
用来存放DMA读取的IO设备的数据,16M

2、ZONE_NORMAL
880M,内核空间可以直接使用的

3、ZONE_HIGHMEM
被称为高端内存,因为内核空间不能直接使用

32位系统,CPU寄存器的长度为43位,所以虚拟地址的最大寻址空间空间为 232,也就是4G。

32位的Linux将这4G虚拟空间按照1:3的比例分别分为 内核空间 和 用户空间。

内核空间映射:

1、直接映射区
896M,内核空间直接映射到对应的ZONE_DMA和ZONE_NORMAL中。为什么叫做直接映射呢? 逻辑地址 直接 减去对应的差值就可以得到对应的物理地址。固定死了。

2、动态映射

为什么要引入动态映射呢?
因为所有物理内存的分配都需要内核程序进行申请,用户进程没有这个权限。所以内核空间一定要能映射到所有的物理内存地址。

那么如果都采用直接映射的话,1G大小逻辑地址的内核空间只能映射1G大小的物理内存。

所以引入了动态映射,动态映射就是 内核空间的逻辑地址可以映射到 物理内存中的ZONE_HIGHMEM(高端内存)中的任何一个地址,并且在对应的物理内存使用完之后,可以再映射其他物理内存地址。

动态映射分为三种
1、动态内存映射: 使用完对应的物理内存后,就可以映射其他物理内存了。

2、永久内存映射: 一个虚拟地址只能映射一个物理地址。如果需要映射其他物理地址,需要解绑。

3、固定内存映射: 只能被某些特定的函数来调用引用物理地址。

动态内存映射和直接映射的区别
动态映射和直接映射的区别就是逻辑地址到物理地址的转化规则。

直接映射

直接映射的规则是死的,一个逻辑地址对应的物理地址是固定的。通过逻辑地址加或者减去一个数,就可以得到对应的物理地址。

动态映射

动态映射是动态的绑定,每个逻辑地址对应的物理地址是动态的,通过页表进行查询

用户空间映射:

用户空间 采用 动态映射,每个虚拟地址可以被映射到一个物理地址,映射到ZONE_HIGHMEM。

为什么用户空间不采用直接映射呢?

因为物理内存是多个进程所有的,每个进程都有一个用户空间。如果采用直接映射的话,对应的物理地址是会冲突的。其用户空间的逻辑地址大小都为3G,所以存在逻辑地址相同,但是对应的物理地址不同。需要通过页表来转化,一个进程会对应一个页表。

分页

Linux采用分页管理,将物理内存分为若干个4KB的page(页),页是分配和回收的基本单位。

页的申请和释放

对于内存中的这若干个page,怎么分配和回收呢?怎么能够快速的分配和回收呢?

伙伴算法

伙伴算法用于分配大内存。

伙伴算法 通过一个链表数组,数组中的链表对应存放的分别为大小为1、2、4、8个页的页框。

分配:
假如需要5个页,就到8个页对应的链表,也就是数组中下标为3的链表,查看是否存在对应的页框,如果存在,就将其取下分配。

如果不存在,就找16个页对应的链表,如果存在大小为16个页的页框,就将其一分为二,将一个分配,然后将另一个加入到下标为3的链表中。

释放
当释放的时候,查看其同伴是否空闲,如果空闲就将其合并,然后继续查看合并后的同伴是否空闲。

如果不空闲,就将其加入到对应的链表上。

伙伴算法的缺点:
伙伴算法分配的最小单位为 1个页,4k,当分配小数据的时候,就会造成资源浪费。

这就引入了slab算法

slab算法

slab维护了三个链表,链表上的节点对应的是一个页 page 4k
1、空闲链表
page还未被使用

2、部分空闲链表
页中部分被占用,还有一些内存空闲

3、用尽链表
页被完全使用

分配:
首先查看部分空闲链表是否可以分配,如果部分空闲链表中的一个页再分配之后,被占满了,就将其加入到用尽链表中。

如果部分空闲链表不能分配,就选择空闲链表,并将对应的页插入到部分空闲链表中。

释放:
如果释放之后,页为空了,就将其加入到空闲链表中

如果释放之后,页不为空,就将其加入到部分空闲链表中

相关文章