【Linux】内存管理机制

Linux内存管理机制

内存的表象层次依次为 逻辑地址------>线性地址----->物理地址

逻辑地址经过段机制转换成线性地址。

线性地址经过页机制转换成物理地址。

Linux将所有程序的段地址定为 0 - 4G(Linux中),所有虽然逻辑地址和线性地址不一样,但是在Linux中它们的值是一样的,可以认为是等价的。

问题:
  1. 进程空间地址如何管理?

  2. 进程地址如何映射到物理内存?

  3. 物理内存如何被管理?

进程内存空间

Linux使用虚拟内存管理技术,使得每个进程都有互不干涉的内存空间。该空间在32位操作系统上是4G的线性虚拟空间。用户所接触到的空间都是虚拟地址空间,无法看见实际的物理空间。使用这种技术不仅能保护操作系统,还可以获得比实际物理空间更大的地址。

  1. 4G虚拟地址空间被分为用户空间和内核空间。用户占0-3G,内核栈3G-4G。用户只能访问自己的虚拟地址空间,不能访问内核空间。之一用户进程进行系统调用(代表用户空间在内核态运行)才可以访问内核空间

  2. 用户空间对应进程,所以当进程切换时,用户空间就会变化。内核空间是内核负责映射的,并不会根据进程进行改变,是固定的。

    内核空间有自己的页表(init_mm.pgd),用户进程有不同的页表

  3. 每个进程的用户空间都是完全独立、互不相干的。

进程内存管理

进程内存管理的对象是进程线性地址空间上的内存镜像,这些内存镜像起始就是进程使用的虚拟内存区域。统一管理这么大的空间非常不容易。为了方便管理,虚拟空间被划分程成许多大小可变的内存区域(一般是4K),这些区域在进程线性地址中像停车场一样有序排列。这些区域的划分原则是将访问的属性一致的(可读、可写、可执行等)放在一起。

查看进程占用的内存区域: cat /proc/ /maps
在这里插入图片描述

每行的数据格式是:

开始地址 终止地址 访问权限 偏移量 主设备号:次设备号 i节点 文件。

在Linux内核中,对应的进程内存区域是:vm_area_struct,内核将每个内存区域作为一个单独的内存对象管理,相应的操作也一致。采用面向对象的方法,可以使vm_area_struct 表示多种类型的内存区域,比如内存映射文件或进程的用户空间栈。

vm_area_struct使用链表来管理虚拟空间,但是为了方便查找,它又使用红黑树来组织内存区域。链表用于遍历全部节点时使用,红黑树适用于地址空间中特定内存区域使用。

进程内存的分配和回收

创建进程 fork()

程序装载 execve()

映射文件 mmap()

动态内存分配 malloc()

都需要分配内存给进程,不过这时申请的不是实际内存,是虚拟内存。准确的是内存区域。

进程对内存区域的分配都会归结于do_mmap()函数上brk()调用单独以系统调用实现,不用do_mmap)

内核使用do_mmap函数创建一个新的线性地址空间。或者说该函数创建了一个新的VMA不是很准确,如果创建的地址区域和一个已经存在的地址空间相邻,并且它们具有相同的权限,那么两个区域将合并。如果不能合并,就是新的VMA了。

同样,释放一个内存区域do_ummap()

如何转换成物理空间

上面所有的进程所需要的 空间都转换成了虚拟空间。当进程需要内存时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,进程并没有获得物理内存,获得的仅仅是对一个新的线性地址区间的使用权。实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由 请求页机制 产生 缺页异常。

该异常是虚拟内存赖以存在的保障。它会告诉内核去真正的为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在地映射到了系统的物理内存上。

这种请求页机制把页面的分配推迟到不能再推迟为止,并不急于把所有的事情都一次性做完。利用了内存的局部性原理,请求页的好处是节省了内存,提高了吞吐率。调用nopage函数。

系统物理内存管理

虽然应用程序操作的对象是虚拟内存,但是处理器操作的是物理内存。当一个应用访问虚拟内存时,首先必须将虚拟地址转化为物理地址,然后处理器才能解析地址访问请求。地址的转化工作需要通过查询页表才能完成,地址转换需要将虚拟内存分段,使每段虚地址都作为一个索引指向页表,而页表项则指向下一个级别的页表或者指向最终的物理页面。

每个进程都有自己的页表,进程描述符的pgd域指向的就是进程的页全局目录。

虚拟地址映射到页之前要分配物理页,并建立页表

物理内存管理(页管理)

Linux内核管理物理内存是通过分页机制实现的,它将整个内存划分成无数个4k大小的页,从而分配和回收内存的基本单位便是内存页。

利用页表可以灵活的分配地址,不需要连续的内存。但是使用连续的内存可以减少对页表的索引,提高效率。

内核采用伙伴的关系来管理页面。分配最小单位只能是2的幂次方的大小。内核中分配空闲页面的基本函数是get_free_page / get_free_pages,它是分配单页或是指定的页面(2、4、8、。。。页)

注意:get_free_page是在内核中分配内存,如果当前内存不够容纳对空间,会以页面大小的倍数为单位,进行扩张或收缩对应的内存空间。

​malloc是在用户空间分配内存。实际上是调用的brk()系统调用

​brk是通过实际的请求修改,而不是页面大小。

​因此,malloc在用户空间分配内存可以以字节为单位,但是在内部会以页为单位。

物理页由struct page描述,系统中所有的页面都存放在mem_map[]数组中,可以通过该数组找到系统中的每一页(空闲或者非空闲),空闲页面可以使用伙伴关系组织的空闲页链表(free_area[MAX_ORDER])来索引。

内核内存使用

Slab

以页为最小分配内存对于内核管理系统中物理内存比较方便,但是实际内存自身使用的内存还是很小的,比如:文件描述符、进程描述符、虚拟内存描述符等行为所需的内存都不到一页。

为了这种需求,Linux采用了一种叫slab分配器的技术,Slab分配器实现非常复杂,核心就是存储池的运用。内存片段被看作对象,当被使用完后,并不直接释放而被缓存到存储池中,留作下次使用。

减少伙伴系统分配算法的调用次数(放置内存碎片过多,找不到大的连续内存)

种叫slab分配器的技术,Slab分配器实现非常复杂,核心就是存储池的运用。内存片段被看作对象,当被使用完后,并不直接释放而被缓存到存储池中,留作下次使用。

减少伙伴系统分配算法的调用次数(放置内存碎片过多,找不到大的连续内存)

Slab并非是脱离伙伴关系而独立存在的一种内存分配方式,slab仍然是建立在页面基础之上,换句话说,Slab将页面(来自于伙伴关系管理的空闲页面链表)撕碎成众多小内存块以供分配,slab中的对象分配和销毁使用kmem_cache_alloc与kmem_cache_free。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值