# 内存管理 - 页面大小
Date: 2020-01-10


## 内存管理发展阶段

内存(RAM)是计算机一种重要资源, 随着应用越来越复杂，`不管存储器有多大,程序都可以把他填满`,这就迫使人们不断寻找解决方案去管理它.

内存管理经历的几个阶段；

- 无存储抽象阶段 (No Memory abstraction)
- 地址空间 (Address Spaces)
- 虚拟内存 (Virtual Memory)

> 1. 无存储抽象阶段: 直接使用物理地址, 简单和划分了用户和操作系统，(驱动程序)使用的内存. 早期的MSDOS采用这种管理方案。
> 2. 通过每个进程基址寄存器和界限寄存器实现动态重定位,映射到不通的物理内存。通过交换技术和空闲内存管理， 处理内存超载
> 3. 构建虚拟地址空间，通过MMU(内存管理单元)完成虚拟内存与物理内存的映射。

* * *

## 分页

分页就是出现虚拟内存这个阶段.虚拟地址按照固定大小划分成若干个页面Page， 物理内存中对应为页框 Page Frame (物理内存最小数据单位), 当程序试图访问内存时，MMU根据虚拟地址映射为物理地址.

* * *

## 页面大小

### 不同处理器体系结构页面大小

| 体系结构 | 最小页面 | 支持大页面 |
| --- | --- | --- |
| 32-bit x86 | 4 KiB | 4 MiB, 2 MiB |
| x86-64 | 4 KiB | 2 MiB, 1 GiB |
| IA-64 (Itanium) | 4 KiB | 8 KiB, 64 KiB, 256 KiB, 1 MiB, 4 MiB, 16 MiB, 256 MiB |
| Power ISA | 4 KiB | 64 KiB, 16 MiB, 16 GiB |
| SPARC v8 with SPARC Reference MMU | 4 KiB | 256 KiB, 16 MiB |
| ARMv7 | 4 KiB | 64 KiB, 1 MiB , 16 MiB |
| UltraSPARC Architecture 2007 | 8 KiB | 64 KiB, 512 KiB , 4 MiB, 32 MiB, 256 MiB, 2 GiB, 16 GiB |

### 页面大小考虑因素([wiki](https://en.wikipedia.org/wiki/Page_\(computer_memory\)#Page_table_size))

页面大小通常有处理器体系结构决定， 目前常用页面大小4KB。选择最佳页面大小要考虑一下几个因素：

- 页表大小: 页面越小时系统需要更多的页面.
- TLB使用: 页面大小越大，TLB可以跟踪更大内存.
- 内部碎片:较大的页面大小会导致大量的内存浪费
- 磁盘存取 页面越大相同数据磁盘传输到内存通常需要时间更少.

### 页面大小设置其他一些考量方式

#### 最优页面计算数学公式

[现代操作系统](https://book.douban.com/subject/27096665/)书中页面最优公式描述:

平均进程大小为`s`个字节，页面大小为`p`个字节。此外，假设每个页面条目都需要`e`个字节。每个进程所需的大概页面数为s/p, 占用页表空间的se/p字节。 最后一页的内存浪费 内部碎片 p/2。 因此，由于 页表和内部碎片损失由总和得出 $$总开销 = s\*e/p + p/2$$

在页面比较小的时候页表项大， 页面比较大的时候内部碎片大, 页面大选主语中间的某个值， 求导数后得出最优页面大小: $$−s\*e/p^2 + 1/2 = 0$$

得到最优页面大小公式 $$p = \\sqrt{2s\*e}$$

当 s=1MB 每个页表项 e=8B,最优页面大小为 4KB（以前典型值为1KB）

#### 其他有趣的算法

stackoverflow 的一个帖子 [Why is the page size of Linux (x86) 4 KB, how is that calculated?](https://stackoverflow.com/questions/11543748/why-is-the-page-size-of-linux-x86-4-kb-how-is-that-calculated)

x86使用`2级页表`将虚拟内存地址转换为物理内存地址。 因此，假设页面目录和页面表都包含 2^x 项, 并且页面大小2^y字节。 为了充分利用 2^32 地址空间，我们有：

$$2^{x}_2^{x}_2^{y} = 2^{32} => 2x+y=32$$

页面目录/表中的每个条目占用4个字节（32位），因此：

$$2^{y}/4 = 2^{x} => y-2 =x $$

y = 12 页面大小 2^y=4KB

同理下面是X86\_64页面计算方法,与X86不同的是使用了四级页表， 48位用于寻址:

$$2^x _2^x_ 2^x _2^x_ 2^y = 2^{48} =>4x + y = 48 $$

每个地址 8 bytes (8 bytes \* 8 bits / byte = 64 bits)

$$2^y / 2^3 = 2^x => y - 3 = x $$

y = 12 页面大小 2^y=4KB

* * *

## Linux Huge Page

通过处理器体系结构的支持可以根据需要将页面大小增加，以期待获得更高的效率.

### 相关参数

#### 内核配置参数

- CONFIG\_HUGETLBFS
- CONFIG\_HUGETLB\_PAGE

#### 内核Huge信息

- /proc/meminfo

| 参数 | 说明 |
| --- | --- |
| HugePages\_Total | 总页面数量 |
| HugePages\_Free | 未使用页面数量 |
| HugePages\_Rsvd | 应用申请未使用数量 |
| HugePages\_Surp | 超过常驻页面使用数量,动态调整方式 |
| Hugepagesize | 每个页的大小单位Kb |
| Hugetlb | 占用内存大小（kB）相当于 HugePages\_Total \* Hugepagesize |

> 关于参数说明可以参考以下两篇文章: [Linux下hugetlbpage使用详解](http://www.lenky.info/archives/2012/03/1219) [HugePages\_Rsvd 是什么意思？](https://feichashao.com/hugepages/)

#### 修改方法

##### 修改sysctl参数

以 Cenos6,7 / RHEL4,5为例

1 设置

```C
# vi /etc/sysctl.conf
vm.nr_hugepages = 10
# sysctl -p
vm.nr_hugepages = 10
```

或临时修改

```C
echo 10 > /proc/sys/vm/nr_hugepages
```

2 验证

```C
cat /proc/meminfo|grep Huge
HugePages_Total:      10
HugePages_Free:       10
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:           20480 kB
```

##### 启动时设置

通过修改grub2参数实现

1 修改`/etc/default/grub`

```C
GRUB_CMDLINE_LINUX="... hugepages=10 hugepagesz=2M default_hugepagesz=4M"
```

2 使用grub2-mkconfig 重建 `grub.cfg`

```C
[root@centosgpt ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
```

3 验证

```C
cat /proc/meminfo|grep Huge
HugePages_Total:      10
HugePages_Free:       10
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:           20480 kB
```

* * *

## 参考

[wiki Page (computer memory)](https://en.wikipedia.org/wiki/Page_%28computer_memory%29#Page_size_trade-off) [Why is the page size of Linux (x86) 4 KB, how is that calculated?](https://stackoverflow.com/questions/11543748/why-is-the-page-size-of-linux-x86-4-kb-how-is-that-calculated) [CentOS / RHEL 6,7 : How to configure hugepages](https://www.thegeekdiary.com/centos-rhel-67-how-to-configure-hugepages/) [CONFIGURING HUGE PAGES IN RED HAT ENTERPRISE LINUX 4 OR 5](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5)

