0%

CSAPP 学习笔记 7 虚拟内存

前言

好久没看CSAPP这本书了。最近在学习redis的AOF重写过程,发现对于涉及的一些概念有些模糊,比如fork、页表、虚拟内存、物理内存等。而且,最近在工作中也经常遇到和内存相关的问题,在利用top命令来排查内存问题的过程中,发现手册里提到的一些内存的概念,自己都不甚清晰。

于是很有必要重新捡起这本书看看,梳理一下书中第9章虚拟内存的相关知识,巩固一下基础。

正文

操作系统的内存是由许多进程共享的,假如进程之间内存没有独立开来,那么很有可能导致进程崩溃。

虚拟内存是现代系统提供的一种对主存的抽象概念,用于将进程间的内存空间相互独立开来。

现代操作系统都使用的是按需页面调度的方式。

从top的manual中可以查看Linux的多种内存类型的定义。

physical memory: a limited resource where code and data must reside when executed or referenced.

optional swap file: where modified (dirty) memory can be saved and later retrieved if too many demands are made on physical memory.

virtual memory: a nearly unlimited resource.

其中,虚拟内存是近似无限大的,它的意义在于:

  1. abstraction, free from physical memory addresses/limits
  2. isolation, every process in a separate address space
  3. sharing, a single mapping can serve multiple needs
  4. flexibility, assign a virtual address to a file

对于第一点和第二点,就是CSAPP中提到的进程内存之间抽象和隔离。

第三点和第四点是什么意思呢?

另外,手册中也提到了不管是哪种内存类型,都是以页面的方式进行管理,通常是4KB。当然,在某些场景可以将页面设置得大一些,称为big pgae.

对于每一个page,在Linux中,都可以划分到某一类中。具体类别如下:

image-20201129171849191

对于物理内存和虚拟内存,可以划分到这四类中任意一个。而对于swap file的内存,只能划分到#1和#3.

Anonymous和File-backed有什么区别:

File-backed mapping maps an area of the process’s virtual memory to files; i.e. reading those areas of memory causes the file to be read. It is the default mapping type. Anonymous mapping maps an area of the process’s virtual memory not backed by any file. The contents are initialized to zero.

top可以显示的关于内存的各种指标,参考手册如下:

      %MEM - simply RES divided by total physical memory
      CODE - the `pgms' portion of quadrant 3
      DATA - the entire quadrant 1 portion of VIRT plus all
             explicit mmap file-backed pages of quadrant 3
      RES  - anything occupying physical memory which, beginning with
             Linux-4.5, is the sum of the following three fields:
             RSan - quadrant 1 pages, which include any
                    former quadrant 3 pages if modified
             RSfd - quadrant 3 and quadrant 4 pages
             RSsh - quadrant 2 pages
      RSlk - subset of RES which cannot be swapped out (any quadrant)
      SHR  - subset of RES (excludes 1, includes all 2 & 4, some 3)
      SWAP - potentially any quadrant except 4
      USED - simply the sum of RES and SWAP
      VIRT - everything in-use and/or reserved (all quadrants)

使用Go写一个简单的hello, world程序:

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
"time"
)

func main() {
fmt.Println("hello, world")
time.Sleep(1 * time.Hour)
}

运行后,通过top的mem视图可以观察该进程的内存情况:

image-20201205114259481

这里的VIRT, RES, CODE, DATA, SHR的单位是KiB。

VIRT:虚拟内存占用大概是700MiB。(感觉有点反直觉)

RES:实际的物理内存占用大概是1.8MiB。

CODE:代码占用的内存是584KiB。

DATA:运行栈和堆的数据大概是102MiB。

SHR:共享内存大概是1MiB。

我感觉实际的内存占用都有点大,能更细粒度地观察内存花在哪里的吗?分析一下。

怎么分析该进程的内存情况呢?

怎么理解这四种内存页呢?mmap这个函数的定义是什么?

top如何保存视图?

(未完待续)