0%

Linux存储管理

大部分现代计算机具有金姿态结构的多级存储体系,即以容量小、价格昂贵、访问速度快的寄存器为塔尖,以容量大、价格便宜、访问速度慢的外部存储(如磁盘)为塔基。

金字塔

简单存储管理数据介绍内存存储的古老方式,在现代操作系统中,大部分使用虚拟存储技术。

简单存储管理技术

特点:程序和数据一次性装入内存

缺点:

  • 无法运行超过内存最大容量的大进程
  • 由于内存最大容量限制,同时运行的进程数受限

同一进程和数据还可能连续存储或离散存储在内存中:

  • 连续存储:分区技术
  • 离散存储:分页、分段、段页

简单存储分区技术

简单存储固定分区

预先将内存空间划分为若干个 固定大小 的分区。

缺点:分区大小如何确认是个问题,系统必须保证大部分程序能够在一个分区中运行,故必然有些程序用不完这么多的内存,导致内存分配出去的内存闲置,即出现 内零头

根据分区大小是否相同还可分为:

  • 等长分区
  • 异长分区

简单存储动态分区

为解决内零头问题,系统根据实际需要,动态划分内存空间给进程。

缺点:系统运行一段时间之后,会出现较小的分区无法分配给系统的情况,即出现 外零头

紧凑技术可缓解外零头。紧凑技术需系统将内存进行整理,这可能需要将内存中的所有程序和数据复制重新规划(如复制到内存一端),需要额外的开销。

简单存储固定分区+动态分区

即伙伴系统。系统总是分配2^i的空闲分区给进程。并且 2^i-1 < 进程需求 <= 2^i。

伙伴系统中相邻的2个相同大小的空闲分区会合并为一个大分区。

简单存储分页技术

特点:分区长度缩小,进程分配若干不连续的内存空间。

优点:彻底消除了外零头,存在少量内零头。

程序的逻辑地址被划分为大小相等的页面,物理地址被划分为大小相等的页框,页面与页框大小相等(为2的次幂字节,范围512B-4KB,一般为1KB)。

同时为了优化页表(每个进程对应的一个页面映射表,简称页表)存储与检索,引入了多级页表。

简单存储分段技术

原理:程序运行对应子任务(称为段),每个子任务是独立的。故为每个子任务分配一个段,每段对应一个分区。于是一个进程可以存储在多个不连续的内存分区中。

需维护段与物理内存的映射关系,即段表。

虚拟存储技术

虚拟存储技术根据 程序执行的局部性原理 (即在较短的时间段内,程序的执行仅限于部分)可运行超过内存最大容量的程序。

虚拟存储技术还可细分为虚拟存储分页技术、虚拟存储分段技术、虚拟存储段页式技术。

虚拟内存技术为每个进程维护一个内存映射表,并非所有虚拟内存都会分配物理内存,只有那些实际使用的部分才会分配物理内存。

虚拟存储映射

页表实际上是存储在CPU的内存管理单元(MMU)中,处理器可直接通过硬件找到要访问的内存。当进程访问的虚拟地址在页表中查不到时,系统会产生一个 缺页异常 ,进入内核空间分配物理内存、更新进程页表,然后再返回用户空间继续执行。

为了加速MMU的查询速度,TLB(Translation Lookaside Buffer)为MMU提供页表的高速缓存。减少进程的上下文切换可提高TLB缓存命中率。

页普遍大小只有4K,为了优化页表存储,Linux提供多级页表和大页机制。

大页机制使用比普通页更大的内存块,常见大小有2MB和1GB。常用在使用大量内存的进程,如Oracle、DPDK等。

虚拟内存到底有多大?

内核空间与用户空间.jpg

在32位的系统中,程序的虚拟内存范围为4G,其中内核空间占用1G(位于最高处),剩余3G是用户空间。

在64位的系统中,程序的虚拟内存范围一般是256TB(2^48),内核空间和用户空间各占128TB(分别位于最高和最低处)。可通过以下命令查看:

1
2
3
# cat /proc/cpuinfo | grep 'address sizes'
address sizes : 40 bits physical, 48 bits virtual
address sizes : 40 bits physical, 48 bits virtual

虚拟内存空间分布

空间分布.jpg

在32位的系统中,用户空间内存从低到高依次为:

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

其中,堆和文件映射段的内存是动态分配的。如使用C标准库的malloc()mmap()就可以分别在堆和文件映射段动态分配内存。

查看虚拟地址空间使用情况

top

1
2
3
4
5
6
7
8
9
10
11
12
# top
top - 16:20:44 up 21 days, 2:21, 1 user, load average: 6.55, 6.21, 6.08
Tasks: 381 total, 1 running, 380 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.5 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 65675956 total, 12292584 free, 562036 used, 52821336 buff/cache
KiB Swap: 32767996 total, 32767996 free, 0 used. 64106612 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
35 root 20 0 0 0 0 S 6.2 0.0 40:10.47 ksoftirqd/5
26839 root 20 0 0 0 0 S 6.2 0.0 0:06.86 kworker/u80:0
1 root 20 0 193848 6192 1992 S 0.0 0.0 6:14.69 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.52 kthreadd

top输出中,内存相关的列与含义如下:

  • VIRT进程虚拟内存大小,只要进程申请过,即便还没有分配物理内存,也计算在内
  • RES常驻内存大小,实际使用的物理内存,不包括SWAP和共享内存
  • SHR共享内存大小,与其他程序共同使用的共享内存、加载的动态链接库以及程序代码段等
  • %MEM进程使用物理内存占系统总内存的百分比。

pmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# pmap -d 23943
23943: java
Address Kbytes Mode Offset Device Mapping
0000000000400000 4 r-x-- 0000000000000000 008:00003 java
0000000000600000 4 rw--- 0000000000000000 008:00003 java
00000000006ca000 132 rw--- 0000000000000000 000:00000 [ anon ]
0000000770000000 2359296 rw--- 0000000000000000 000:00000 [ anon ]
000000346e800000 128 r-x-- 0000000000000000 008:00003 ld-2.12.so
000000346ea1f000 4 r---- 000000000001f000 008:00003 ld-2.12.so
000000346ea20000 4 rw--- 0000000000020000 008:00003 ld-2.12.so
000000346ea21000 4 rw--- 0000000000000000 000:00000 [ anon ]
000000346ec00000 524 r-x-- 0000000000000000 008:00003 libm-2.12.so
000000346ec83000 2044 ----- 0000000000083000 008:00003 libm-2.12.so
000000346ee82000 4 r---- 0000000000082000 008:00003 libm-2.12.so
000000346ee83000 4 rw--- 0000000000083000 008:00003 libm-2.12.so
000000346f000000 1576 r-x-- 0000000000000000 008:00003 libc-2.12.so
000000346f18a000 2048 ----- 000000000018a000 008:00003 libc-2.12.so
000000346f38a000 16 r---- 000000000018a000 008:00003 libc-2.12.so
000000346f38e000 4 rw--- 000000000018e000 008:00003 libc-2.12.so
...

其实pmap是依据以下两个文件进行解析的:

1
2
/proc/pid/maps
/proc/pid/smaps

查看进程的缺页中断

1
2
ps -o majflt,minflt -C <program_name>
ps -o majflt,minflt -p <pid>

其中, majflt 代表 major fault ,指大错误, minflt 代表 minor fault ,指小错误。这两个数值表示一个进程自启动以来所发生的缺页中断的次数。其中 majflt 与 minflt 的不同是, majflt 表示需要读写磁盘,可能是内存对应页面在磁盘中需要 load 到物理内存中,也可能是此时物理内存不足,需要淘汰部分物理页面至磁盘中。

如果进程的内核态 CPU 使用过多,其中一个原因就可能是单位时间的缺页中断次数多个,可通过以上命令来查看。

本节为《操作系统原理与Linux实例设计》读书笔记。
参考:
https://blog.csdn.net/u010150046/article/details/72630262#t1
https://blog.csdn.net/xiaokang123456kao/article/details/73849310