内存映射

只有实际使用的虚拟内存才分配物理内存,分配后的物理内存通过内存映射管理。

为了完成内存映射,内核为每个进程维护一张页表,记录虚拟地址与物理地址的映射关系。

页表存储在 CPU 的内存管理单元 MMU 中。

TLB(Translation Lookaside Buffer)是 MMU 中页表的高速缓存。

MMU 以页(大小通常为 4KB)为单位管理内存。

为解决页表项过多的问题,e.g. 仅 32 位系统就需 100 多万个页表项(4GB/4KB),Linux 提供了两种机制:多级页表和大页。

虚拟内存空间分布

在这里插入图片描述
用户空间内存,从低到高分别为:

  1. 只读段,包括代码和常量等。
  2. 数据段,包括全局变量等。
  3. 堆,包括动态分配的内存,从低地址向上增长。
  4. 文件映射段,包括动态库、共享内存等,从高地址开始向下增长。
  5. 栈,包括局部变量和函数调用的上下文等。栈大小是固定的,一般是 8MB。

堆和文件映射段的内存是动态分配的。

内存分配与回收

C 标准库采用 malloc() 分配内存,对应到系统调用,有两种实现方式,brk 和 mmap()。

brk() 分配小内存(小于 128K),通过移动堆顶的位置实现,释放后不会立刻归还内存,而是被缓存起来,供重复使用。

mmap() 分配大内存(大于 128K),在文件映射段找一块空闲内存分配出去,释放后直接归还系统,所以每次都会发生缺页异常。

两种调用发生后,不会真正分配内存,而是在内存被首次访问时,通过缺页异常进入内核,由内核分配。

Linux 使用伙伴系统管理内存分配,以页为单位,如果遇到比页小的对象(如不到 1K),对于用户空间,malloc 通过 brk() 分配;对于内核空间,通过 slab 分配器,可将 slab 看作构建在伙伴系统上的一个缓存。

当内存紧张时,系统会通过一系列机制回收内存:

  • 回收缓存,如 LRU 算法;
  • 回收不常访问的内存,把不常使用的内存通过交换分区直接写到磁盘;
  • 杀死进程,通过 OOM(Out of Memory)直接杀死占用大量内存的进程。

查看内存

查看系统内存:

# 注意不同版本的 free 输出可能会有所不同
$ free
                 total             used         free               shared  buff/cache   available
Mem:        4039436      570832     2195068       11972     1273536     3108340
Swap:       1533948           0          1533948
  • total:总内存大小;
  • used:已使用内存的大小,包含了共享内存;
  • free:未使用内存的大小;
  • shared:共享内存的大小;
  • buff/cache:缓存和缓存区的大小;
  • available:新进程可用内存的大小,包含未使用内存和可回收的缓存。

查看进程内存:

# 按下 M 切换到内存排序
$ top

top - 21:34:30 up  1:03,  1 user,  load average: 0.05, 0.21, 0.16
Tasks: 206 total,   2 running, 204 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.7 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem : 25.0/4039436  [||||||||||||||                                       ]
KiB Swap:  0.0/1533948  [                                                     ]

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND     
 1695 root      20   0  476544  76896  34408 R   2.7  1.9   0:04.31 Xorg        
 4572 yjp       20   0 1244828  82788  51872 S   2.3  2.0   0:04.78 compiz      
 4855 yjp       20   0  663612  35588  28484 S   1.3  0.9   0:00.66 gnome-term+ 
 1714 root      10 -10  393912  50248   7464 S   0.7  1.2   0:06.27 ovs-vswitc+ 
 2139 root      20   0  795780  67780  39256 S   0.3  1.7   0:10.40 dockerd    
  • VIRT:进程虚拟内存大小,包括进程申请过的内存,即便没有分配;
  • RES:常驻内存大小,即实际使用的物理内存大小,不包括 Swap 和共享内存;
  • SHR:共享内存大小,如与其它进程共同使用的共享内存、加载的动态链接库和程序代码段等;
  • %MEM:进程使用的物理内存占系统内存的百分比。

Buffer 和 Cache

Buffer 是对磁盘数据的缓存,Cache 是文件数据的缓存,它们会用在读请求,也会用在写请求中。

验证:

磁盘和文件写

# 清理文件页、目录项、Inodes 等各种缓存
$ echo 3 > /proc/sys/vm/drop_caches

终端1,读取随机设备,生成一个 500MB 大小的文件:

$ dd if=/dev/urandom of=/tmp/file bs=1M count=500

终端2,查看:

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0      0 7499460   1344 230484    0    0     0     0   29  145  0  0 100  0  0
 1  0      0 7338088   1752 390512    0    0   488     0   39  558  0 47 53  0  0
 1  0      0 7158872   1752 568800    0    0     0     4   30  376  1 50 49  0  0
 1  0      0 6980308   1752 747860    0    0     0     0   24  360  0 50 50  0  0
 0  0      0 6977448   1752 752072    0    0     0     0   29  138  0  0 100  0  0
 0  0      0 6977440   1760 752080    0    0     0   152   42  212  0  1 99  1  0
...
 0  1      0 6977216   1768 752104    0    0     4 122880   33  234  0  1 51 49  0
 0  1      0 6977440   1768 752108    0    0     0 10240   38  196  0  0 50 50  0

cache 增长,buff 几乎不变。

终端1:

# 首先清理缓存
$ echo 3 > /proc/sys/vm/drop_caches
# 然后运行 dd 命令向磁盘分区 /dev/sdb1 写入 2G 数据
$ dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048

终端2:

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
1  0      0 7584780 153592  97436    0    0   684     0   31  423  1 48 50  2  0
 1  0      0 7418580 315384 101668    0    0     0     0   32  144  0 50 50  0  0
 1  0      0 7253664 475844 106208    0    0     0     0   20  137  0 50 50  0  0
 1  0      0 7093352 631800 110520    0    0     0     0   23  223  0 50 50  0  0
 1  1      0 6930056 790520 114980    0    0     0 12804   23  168  0 50 42  9  0
 1  0      0 6757204 949240 119396    0    0     0 183804   24  191  0 53 26 21  0
 1  1      0 6591516 1107960 123840    0    0     0 77316   22  232  0 52 16 33  0

buff 增长,cache 几乎不变。

磁盘和文件读

结果同磁盘和文件写。

参考
倪朋飞. Linux 性能优化实战.

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐