# Chapter 9: Virtual Memory 閱讀筆記
本文旨在記錄 *Computer Systems: A Programmer's Perspective* (CS:APP) 一書第七章閱讀心得,該書是 CMU 的計算機系統概論的教材 (難度相當於台灣的大學高年級),該書的簡體中文翻譯名稱為《深入理解計算機系統》。
CS:APP 亦是 [Linux Kernel Internals 2024 Spring](https://wiki.csie.ncku.edu.tw/linux/schedule) 課程指定教材,並一同收錄於
[Linux Kernel Internals 2024 Spring Collections](https://hackmd.io/@Kuanch/linux2024-collection)。
## 9.7 Case Study: The Intel Core i7/Linux Memory Syste
### 9.7.2 Linux Virtual Memory System
下圖為 Linux 行程當中記憶體配置的示意圖:

另一張圖將 MMU 各個部分和 Linux 核心程式碼連結:

我們可以在 `include/linux/mm_types.h` 找到相對應的結構 `vm_area_struct`
```c
/*
* This struct describes a virtual memory area. There is one of these
* per VM-area/task. A VM area is any part of the process virtual memory
* space that has a special rule for the page-fault handlers (ie a shared
* library, the executable area etc).
*/
struct vm_area_struct {
/* The first cache line has the info for VMA tree walking. */
union {
struct {
/* VMA covers [vm_start; vm_end) addresses within mm */
unsigned long vm_start;
unsigned long vm_end;
};
#ifdef CONFIG_PER_VMA_LOCK
struct rcu_head vm_rcu; /* Used for deferred freeing. */
#endif
};
struct mm_struct *vm_mm; /* The address space we belong to. */
pgprot_t vm_page_prot; /* Access permissions of this VMA. */
/*
* Flags, see mm.h.
* To modify use vm_flags_{init|reset|set|clear|mod} functions.
*/
union {
const vm_flags_t vm_flags;
vm_flags_t __private __vm_flags;
};
// ....skip
}
```
注意到 `mm_struct` 並不直接帶有 `vm_area_struct`,而是後者透過 `vm_area_struct->vm_mm` 儲存。
若深入追究,我們可以在 `copy_process()` 下看到一系列初始化 VMA 的操作,如 `copy_mm()`,而其下之 `copy_mm()` 有以下操作
```c
static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)
{
// ... skip
if (clone_flags & CLONE_VM) {
mmget(oldmm);
mm = oldmm;
} else {
mm = dup_mm(tsk, current->mm);
if (!mm)
return -ENOMEM;
}
// ... skip
}
```
`dup_mm()` 其內的 `dup_mmap()` 就能看到如上圖其指定所屬 `mm_struct` 和一般鏈結多個 vma 的操作:
```c
#ifdef CONFIG_MMU
static __latent_entropy int dup_mmap(struct mm_struct *mm,
struct mm_struct *oldmm)
{
struct vm_area_struct *mpnt, *tmp;
...
for_each_vma(vmi, mpnt) {
...
tmp->vm_mm = mm;
...
if (file) {
...
/* insert tmp into the share list, just after mpnt */
vma_interval_tree_insert_after(tmp, mpnt, &mapping->i_mmap);
...
}
...
}
...
}
```