Skip to content

Commit

Permalink
doc: linux memory management;
Browse files Browse the repository at this point in the history
  • Loading branch information
MarsonShine committed Feb 25, 2025
1 parent f7a1b94 commit 2d1ef03
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 0 deletions.
40 changes: 40 additions & 0 deletions NET-Memory-Management-For-BCPS/Low-Level-Memory-Management.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,46 @@ Windows上初始线程栈大小(包括保留和初始提交)来源于可执

#### Linux内存管理

直到不久之前,任何关于 .NET 的书籍中提到 Linux 时,通常都仅限于 Mono 项目。然而,时代已经改变。随着 .NET Core(包括 .NET 5+)环境的出现,已无法忽略非 Windows 系统。此外,随着 Linux 在容器领域的广泛应用,在非 Windows 机器上运行 .NET 变得越来越流行。因此,我们将重点关注 .NET Core 和 .NET 5+ 运行时的实现。

由于 Linux 使用相同的硬件技术,包括页(pages)、内存管理单元(MMU)和转换后备缓冲(TLB),很多基础知识已经在前面的小节中介绍过了。因此,在这里,我们只关注一些关键的不同之处。随着越来越多的人需要在非 Windows .NET 环境中部署应用程序,了解一些 Linux 的基础知识将非常有益。

主流的 Linux 发行版同样采用虚拟内存的概念。每个进程的虚拟地址空间限制与 Windows 类似,具体如下表 2-5 所示。

**表 2-5**. Linux 上的虚拟地址空间大小限制(用户/内核)

| 处理器位数 | Linux 32 位 | Linux 64 位 |
| ---------- | ---------------- | ----------- |
| 32 位 | 3/1, 2/2, 1/3 GB | - |
| 64 位 | - | 128/128 TB* |

*采用 48 位标准地址编址*

与 Windows 类似,Linux 的基本内存管理单元是 **页(page)**,其大小通常为 **4 KB**。页面的状态可以分为以下三种:

- **空闲(Free):** 未被任何进程或系统使用。
- **已分配(Allocated):** 被分配给某个进程。
- **共享(Shared):** 被某个进程保留,但可以与其他进程共享。典型情况包括二进制程序映像、内存映射文件以及系统范围的库和资源。

与 Windows 相比,Linux 的内存管理提供了一个更清晰的进程内存使用视图。在 Windows 中,存在 **隐式的页面保留(reservation)** 机制,而 Linux 没有这个阶段,尽管它仍然可以显式存在。Linux 采用 **懒分配(lazy allocation)** 机制来管理内存。当进程分配内存时,虽然系统会记录该分配,但并不会立即分配实际的物理资源(这类似于 Windows 的“预留”)。只有当进程**实际访问该内存区域**时,物理内存才会真正被分配。如果需要在性能关键场景下主动分配这些页面,可以通过访问它们(例如读取至少一个字节)来“触碰”它们,以确保资源就绪。

了解了可能的页面状态后,我们可以进一步探讨 Linux 进程的内存是如何划分的。关于这一点,存在不少混淆,因为许多基于 Linux 的工具对这一话题的描述略有不同。进程的内存使用情况可以通过以下几个术语进行衡量:

- **虚拟内存(Virtual,某些工具标记为 vsz):**进程当前预留的虚拟地址空间总大小。在常见的 `top` 工具中,该值显示在 **VIRT** 列中。
- **常驻内存(Resident Set Size, RSS)**:进程当前驻留在物理内存中的页面空间。一些常驻页面可能会在进程间共享(例如文件映射页面),因此这一指标相当于 Windows 上的“工作集(working set)”。在 `top` 工具中,该值显示在 RES 列中。RSS 进一步细分为:
- **私有常驻页面(Private resident pages):** 这些是所有为该进程保留的匿名常驻页面(由 **MM_ANONPAGES** 内核计数器指示)。这与 Windows 上的“私有工作集(private working set)”类似。
- **共享常驻页面(Shared resident pages):** 包括**文件映射页面(file-backed pages,受 MM_FILEPAGES 内核计数器指示)** 以及匿名共享页面,相当于 Windows 的“共享工作集(shared working set)”。在 `top` 工具中,该值显示在 `SHR` 列中。
- **私有内存(Private):** 进程的所有私有页面。在 `top` 工具中,这一值显示在 `DATA` 列中。请注意,该指标表示已预留的内存,并不表示其中有多少内存已经被访问(即“触碰”过),因此并不一定已经变为常驻内存。这相当于 Windows 上的 **private bytes(私有字节)**
- **交换内存(swapped):** 进程的部分虚拟内存被存入 `swap` 文件的部分。

![](asserts/2-19.png)

**图 2-19** 以图示的方式展示了这些指标之间的关系,它们是互相重叠的集合。

这一切确实很复杂。就像在 Windows 上一样,回答“是什么占用了 .NET 进程的内存”并不简单。最合理的做法是关注 “私有常驻页面(Private resident pages)”,因为它能反映进程实际消耗的宝贵 RAM 资源。

> 在 Windows 上,内存分配的**粒度(allocation granularity)****64 KB**,而在 Linux 上,仅受**页面大小(page size)**限制,在大多数情况下为 **4 KB**
#### Linux内存布局

## NUMA 和 CPU 组
Expand Down
Binary file added NET-Memory-Management-For-BCPS/asserts/2-19.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2d1ef03

Please sign in to comment.