Skip to content

Commit

Permalink
内存管理
Browse files Browse the repository at this point in the history
  • Loading branch information
Qianhongbo committed Nov 20, 2022
1 parent 3cdf006 commit dcba216
Showing 1 changed file with 137 additions and 0 deletions.
137 changes: 137 additions & 0 deletions source/_posts/Backend/operating-system/内存管理.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
title: 内存管理
date: 2022-11-19 22:32:12
categories:
- Backend
tags:
- Operating System
- 操作系统
---

# 内存管理

## 虚拟内存

### 引入的目的

把进程所使用的地址「隔离」开来,即让操作系统为每个进程分配独立的一套「**虚拟地址**」,互不干涉。但是有个前提每个进程都不能访问物理地址,至于虚拟地址最终怎么落到物理内存里,对进程来说是透明的,操作系统已经把这些都安排的明明白白了。

### 实现

> **操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。**
>
> 如果程序要访问虚拟地址的时候,由操作系统转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了。
![](https://img-blog.csdnimg.cn/72ab76ba697e470b8ceb14d5fc5688d9.png)

### 操作系统管理虚拟地址与物理地址之间的关系:**分页和分段**

#### 内存分段 Segmentation

分段的好处是能产生连续的内存空间,但是会出现外部内存碎片和内存交换过大的问题。

段选择因子和段内偏移量:

- **段选择子**就保存在段寄存器里面。段选择子里面最重要的是**段号**,用作段表的索引。**段表**里面保存的是这个**段的基地址、段的界限和特权等级**等。
- 虚拟地址中的**段内偏移量**应该位于 0 和段界限之间,如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址。

![](https://img-blog.csdnimg.cn/c5e2ab63e6ee4c8db575f3c7c9c85962.png)

但它也有一些不足之处:

- 第一个就是**内存碎片**的问题。(外部内存碎片)原因:些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求。解决方法是内存交换。
- 第二个就是**内存交换的效率低**的问题。原因:经常会产生外部碎片,需要进行和硬盘之间的内存交换,重新Swap区域,会产生性能瓶颈。

#### 内存分页 Paging

**分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小**。这样一个连续并且尺寸固定的内存空间,我们叫*****Page*)。在 Linux 下,每一页的大小为 `4KB`

![](https://img-blog.csdnimg.cn/388a29f45fe947e5a49240e4eff13538.png)

##### 缺页异常

而当进程访问的虚拟地址在页表中查不到时,系统会产生一个**缺页异常**,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

##### 内部碎片

**采用了分页,页与页之间是紧密排列的,所以不会有外部碎片。**但是,因为内存分页机制分配内存的最小单位是一页,即使程序不足一页大小,我们最少只能分配一个页,所以页内会出现内存浪费,所以针对**内存分页机制会有内部内存碎片**的现象。

##### 换入换出

如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为**换出***Swap Out*)。一旦需要的时候,再加载进来,称为**换入***Swap In*)。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,**内存交换的效率就相对比较高。**

![](https://img-blog.csdnimg.cn/8f187878c809414ca2486b0b71e8880e.png)

##### 缺陷

**空间**上的缺陷。

在 32 位的环境下,虚拟地址空间共有 4GB,假设一个页的大小是 4KB(2^12),那么就需要大约 100 万 (2^20) 个页,每个「页表项」需要 4 个字节大小来存储,那么整个 4GB 空间的映射就需要有 `4MB` 的内存来存储页表。

这 4MB 大小的页表,看起来也不是很大。但是要知道每个进程都是有自己的虚拟地址空间的,也就说都有自己的页表。

那么,`100` 个进程的话,就需要 `400MB` 的内存来存储页表,这是非常大的内存了,更别说 64 位的环境了。

##### 多级页表

将页表(一级页表)分为 `1024` 个页表(二级页表),每个表(二级页表)中包含 `1024` 个「页表项」,形成**二级分页**

![](https://img-blog.csdnimg.cn/19296e249b2240c29f9c52be70f611d5.png)

> 如果使用了二级分页,一级页表就可以覆盖整个 4GB 虚拟地址空间,但**如果某个一级页表的页表项没有被用到,也就不需要创建这个页表项对应的二级页表了,即可以在需要时才创建二级页表**
> 那么为什么不分级的页表就做不到这样节约内存呢?
>
> 我们从页表的性质来看,保存在内存中的页表承担的职责是将虚拟地址翻译成物理地址。假如虚拟地址在页表中找不到对应的页表项,计算机系统就不能工作了。所以**页表一定要覆盖全部虚拟地址空间,不分级的页表就需要有 100 多万个页表项来映射,而二级分页则只需要 1024 个页表项**
> 对于 64 位的系统,两级分页肯定不够了,就变成了四级目录
##### TLB

多级页表虽然解决了空间上的问题,但是虚拟地址到物理地址的转换就多了几道转换的工序,这显然就降低了这俩地址转换的速度,也就是带来了时间上的开销。

我们就可以利用这一特性,把最常访问的几个页表项存储到访问速度更快的硬件,于是计算机科学家们,就在 CPU 芯片中,加入了一个专门存放程序最常访问的页表项的 Cache,这个 Cache 就是 TLB(*Translation Lookaside Buffer*) ,通常称为页表缓存、转址旁路缓存、快表等。

![](https://img-blog.csdnimg.cn/a3cdf27646b24614a64cfc5d7ccffa35.png)

#### 段页式内存管理

##### 管理方式

- 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制;
- 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页;

这样,地址结构就由**段号、段内页号和页内位移**三部分组成。

![](https://img-blog.csdnimg.cn/8904fb89ae0c49c4b0f2f7b5a0a7b099.png)

### Linux的虚拟地址空间分布

在 Linux 操作系统中,虚拟地址空间的内部又被分为**内核空间和用户空间**两部分,不同位数的系统,地址空间的范围也不同。比如最常见的 32 位和 64 位系统,如下所示:

![](https://img-blog.csdnimg.cn/3a6cb4e3f27241d3b09b4766bb0b1124.png)

- `32` 位系统的内核空间占用 `1G`,位于最高处,剩下的 `3G` 是用户空间;
- `64` 位系统的内核空间和用户空间都是 `128T`,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的。

> 内核空间与用户空间的区别:
>
> - 进程在用户态时,只能访问用户空间内存;
> - 只有进入内核态后,才可以访问内核空间的内存;
虽然每个进程都各自有独立的虚拟内存,但是**每个虚拟内存中的内核地址,其实关联的都是相同的物理内存**。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。

![](https://img-blog.csdnimg.cn/48403193b7354e618bf336892886bcff.png)

### 用户空间分布

![](https://img-blog.csdnimg.cn/img_convert/b4f882b9447760ce5321de109276ec23.png)

通过这张图你可以看到,用户空间内存,从**低到高**分别是 6 种不同的内存段:

- 程序文件段(.text),包括二进制可执行代码;
- 已初始化数据段(.data),包括静态常量;
- 未初始化数据段(.bss),包括未初始化的静态变量;
- 堆段,包括动态分配的内存,从低地址开始向上增长;
- 文件映射段,包括动态库、共享内存等,从低地址开始向上增长;
- 栈段,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 `8 MB`。当然系统也提供了参数,以便我们自定义大小;

0 comments on commit dcba216

Please sign in to comment.