Linux 系统内存管理全解析
1. 内核空间内存布局
内核内存的管理方式较为直接,它不采用按需分页的方式,这意味着使用
kmalloc()
或类似函数进行的每次内存分配,都会对应真实的物理内存,且内核内存不会被丢弃或换出。
在某些架构中,内核启动时会在日志消息里显示内存映射的概要信息。以下是一个 32 位 Arm 设备(BeagleBone Black)的示例:
Memory: 511MB = 511MB total
Memory: 505980k/505980k available, 18308k reserved, 0K highmem
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
vmalloc : 0xe0800000 - 0xff000000 ( 488 MB)
lowmem : 0xc0000000 - 0xe0000000 ( 512 MB)
pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)
modules : 0xbf800000 - 0xbfe00000 ( 6 MB)
.text : 0xc0008000 - 0xc0763c90 (7536 kB)
.init : 0xc0764000 - 0xc079f700 ( 238 kB)
.data : 0xc07a0000 - 0xc0827240 ( 541 kB)
.bss : 0xc0827240 - 0xc089e940 ( 478 kB)
505,980 KiB 这个可用内存数值,是内核开始执行但尚未进行动态内存分配时所看到的空闲内存量。
内核空间内存的使用者包括以下几类:
-
内核自身
:也就是在启动时从内核映像文件加载的代码和数据,这些信息在前面的内核日志中的
.text
、
.init
、
.data
和
.bss
段有所体现。其中,
.init
段在内核完成初始化后会被释放。
-
通过 slab 分配器分配的内存
:用于各种内核数据结构,包括使用
kmalloc()
进行的内存分配,这些内存来自
lowmem
区域。
-
通过
vmalloc()
分配的内存
:通常用于分配比
kmalloc()
所能提供的更大块的内存,这些内存位于
vmalloc
区域。
-
设备驱动程序的映射
:用于访问各种硬件的寄存器和内存,可通过读取
/proc/iomem
查看。这些映射也来自
vmalloc
区域,但由于它们映射到的是主系统内存之外的物理内存,因此不会占用实际的内存。
-
内核模块
:被加载到标记为
modules
的区域。
-
其他未被其他地方记录的底层内存分配
。
2. 内核使用了多少内存
要精确回答内核使用了多少内存这个问题比较困难,但可以通过以下方法尽量接近准确答案。
首先,可以从前面展示的内核日志中查看内核代码和数据所占用的内存,也可以使用
size
命令,示例如下:
$ arm-poky-linux-gnueabi-size vmlinux
text data bss dec hex filename
9013448 796868 8428144 18238460 1164bfc vmlinux
通常,这里显示的内核静态代码和数据段所占用的内存量,与总内存量相比是比较小的。若情况并非如此,则需要检查内核配置,移除不需要的组件。
还可以通过读取
/proc/meminfo
获取更多内存使用信息:
# cat /proc/meminfo
MemTotal: 509016 kB
MemFree: 410680 kB
Buffers: 1720 kB
Cached: 25132 kB
SwapCached: 0 kB
Active: 74880 kB
Inactive: 3224 kB
Active(anon): 51344 kB
Inactive(anon): 1372 kB
Active(file): 23536 kB
Inactive(file): 1852 kB
Unevictable: 0 kB
Mlocked: 0 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 509016 kB
LowFree: 410680 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 16 kB
Writeback: 0 kB
AnonPages: 51248 kB
Mapped: 24376 kB
Shmem: 1452 kB
Slab: 11292 kB
SReclaimable: 5164 kB
SUnreclaim: 6128 kB
KernelStack: 1832 kB
PageTables: 1540 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 254508 kB
Committed_AS: 734936 kB
VmallocTotal: 499712 kB
VmallocUsed: 29576 kB
VmallocChunk: 389116 kB
proc(5)
手册页对这些字段都有详细描述。内核内存使用量是以下各项的总和:
-
Slab
:slab 分配器分配的总内存。
-
KernelStack
:执行内核代码时使用的栈空间。
-
PageTables
:用于存储页表的内存。
-
VmallocUsed
:
vmalloc()
分配的内存。
对于 slab 分配,可以通过读取
/proc/slabinfo
获取更多信息;对于
vmalloc
区域的分配,可以通过
/proc/vmallocinfo
查看详细情况。不过,要确切了解是哪个子系统进行了分配以及原因,需要对内核及其子系统有深入了解。
使用
lsmod
可以查看模块代码和数据所占用的内存空间:
# lsmod
Module Size Used by
g_multi 47670 2
libcomposite 14299 1 g_multi
mt7601Usta 601404 0
由于存在未记录的底层内存分配,难以准确统计内核空间的内存使用情况。当把已知的内核和用户空间的所有内存分配相加时,会发现有部分内存缺失。
3. 用户空间内存布局
Linux 对用户空间采用延迟分配策略,只有当程序访问内存时,才会映射物理内存页。例如,使用
malloc(3)
分配 1 MiB 的缓冲区,只会返回一个指向内存地址块的指针,而不会分配实际的物理内存。页表项中会设置一个标志,使得任何读写访问都会被内核捕获,这就是所谓的页错误。只有在此时,内核才会尝试找到一个物理内存页,并将其添加到进程的页表映射中。
以下是一个简单的程序示例,用于演示这一过程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#define BUFFER_SIZE (1024 * 1024)
void print_pgfaults(void)
{
int ret;
struct rusage usage;
ret = getrusage(RUSAGE_SELF, &usage);
if (ret == -1) {
perror("getrusage");
} else {
printf("Major page faults %ld\n", usage.ru_majflt);
printf("Minor page faults %ld\n", usage.ru_minflt);
}
}
int main(int argc, char *argv[])
{
unsigned char *p;
printf("Initial state\n");
print_pgfaults();
p = malloc(BUFFER_SIZE);
printf("After malloc\n");
print_pgfaults();
memset(p, 0x42, BUFFER_SIZE);
printf("After memset\n");
print_pgfaults();
memset(p, 0x42, BUFFER_SIZE);
printf("After 2nd memset\n");
print_pgfaults();
return 0;
}
运行该程序,输出结果如下:
Initial state
Major page faults 0
Minor page faults 172
After malloc
Major page faults 0
Minor page faults 186
After memset
Major page faults 0
Minor page faults 442
After 2nd memset
Major page faults 0
Minor page faults 442
程序环境初始化后会遇到 172 次小页错误,调用
getrusage(2)
时还会再遇到 14 次(这些数字会因架构和 C 库版本的不同而有所变化)。关键在于填充内存数据时页错误的增加:442 - 186 = 256。缓冲区为 1 MiB,即 256 页。第二次调用
memset(3)
没有产生额外的页错误,因为所有页面都已完成映射。
页错误分为小页错误和大页错误。小页错误时,内核只需找到一个物理内存页并将其映射到进程地址空间;大页错误通常发生在虚拟内存映射到文件时,例如使用
mmap(2)
。此时,内核不仅要找到一个内存页并进行映射,还需要从文件中读取数据填充该页,因此大页错误在时间和系统资源方面的开销更大。
4. 进程内存映射
每个用户空间的运行进程都有一个可以查看的进程映射,这些内存映射能让我们了解程序的内存分配情况以及它所链接的共享库。可以通过
proc
文件系统查看进程的内存映射,以下是
init
进程(PID 1)的映射示例:
# cat /proc/1/maps
00008000-0000e000 r-xp 00000000 00:0b 23281745 /sbin/init
00016000-00017000 rwxp 00006000 00:0b 23281745 /sbin/init
00017000-00038000 rwxp 00000000 00:00 0 [heap]
b6ded000-b6f1d000 r-xp 00000000 00:0b 23281695 /lib/libc-2.19.so
b6f1d000-b6f24000 ---p 00130000 00:0b 23281695 /lib/libc-2.19.so
b6f24000-b6f26000 r-xp 0012f000 00:0b 23281695 /lib/libc-2.19.so
b6f26000-b6f27000 rwxp 00131000 00:0b 23281695 /lib/libc-2.19.so
b6f27000-b6f2a000 rwxp 00000000 00:00 0
b6f2a000-b6f49000 r-xp 00000000 00:0b 23281359 /lib/ld-2.19.so
b6f4c000-b6f4e000 rwxp 00000000 00:00 0
b6f4f000-b6f50000 r-xp 00000000 00:00 0 [sigpage]
b6f50000-b6f51000 r-xp 0001e000 00:0b 23281359 /lib/ld-2.19.so
b6f51000-b6f52000 rwxp 0001f000 00:0b 23281359 /lib/ld-2.19.so
beea1000-beec2000 rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
前两列显示每个映射的起始和结束虚拟地址以及权限,权限说明如下:
| 权限符号 | 含义 |
| ---- | ---- |
| r | 读 |
| w | 写 |
| x | 执行 |
| s | 共享 |
| p | 私有(写时复制) |
如果映射与文件相关,文件名会显示在最后一列,第三、四、五列分别包含文件的偏移量、块设备号和文件的索引节点。大多数映射指向程序本身及其链接的库。程序有两个可以分配内存的区域,分别标记为
[heap]
和
[stack]
。使用
malloc
分配的内存通常来自
[heap]
(除了非常大的分配,后续会详细说明),栈上的分配则来自
[stack]
。这两个区域的最大大小由进程的
ulimit
控制:
-
堆
:
ulimit -d
,默认无限制。
-
栈
:
ulimit -s
,默认 8 MiB。
超过限制的内存分配会触发
SIGSEGV
信号而被拒绝。
当内存不足时,内核可能会丢弃映射到文件且为只读的页面。如果再次访问这些页面,会引发大页错误,内核将从文件中重新读取这些页面。
5. 交换机制
交换机制的目的是预留一些存储空间,内核可以将未映射到文件的内存页面放置到这里,从而释放内存供其他用途使用。交换文件的大小会增加物理内存的有效容量,但这并非万能之策。将页面复制到交换文件和从交换文件复制回来会产生开销,当系统的实际内存不足以满足工作负载需求,交换操作成为主要活动时,就会出现所谓的磁盘抖动现象。
嵌入式设备很少使用交换机制,因为它与闪存存储不兼容,频繁的写入会很快损坏闪存。不过,可以考虑使用压缩 RAM(zram)进行交换。
5.1 交换到压缩内存(zram)
zram
驱动会创建基于 RAM 的块设备,如
/dev/zram0
、
/dev/zram1
等。写入这些设备的页面会在存储前进行压缩,压缩比通常在 30% 到 50% 之间,预计可使空闲内存总体增加约 10%,但会增加处理负担和功耗。
要启用
zram
,需要在内核配置中设置以下选项:
CONFIG_SWAP
CONFIG_CGROUP_MEM_RES_CTLR
CONFIG_CGROUP_MEM_RES_CTLR_SWAP
CONFIG_ZRAM
然后在
/etc/fstab
中添加以下内容,以便在启动时挂载
zram
:
/dev/zram0 none swap defaults zramsize=<size in bytes>,swapprio=<swap partition priority>
可以使用以下命令开启和关闭交换功能:
# swapon /dev/zram0
# swapoff /dev/zram0
交换内存到
zram
比交换到闪存存储要好,但这两种技术都不能替代足够的物理内存。
6. 使用 mmap 进行内存映射
进程启动时,会将一定量的内存映射到程序文件的文本(代码)和数据段,以及它所链接的共享库。进程可以在运行时使用
malloc(3)
在堆上分配内存,通过局部作用域变量和
alloca(3)
在栈上分配内存,还可以使用
dlopen(3)
动态加载库。这些映射都由内核负责管理,但进程也可以使用
mmap(2)
显式地操作其内存映射:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
该函数将文件描述符为
fd
的文件从偏移量
offset
开始的
length
字节内存映射到进程地址空间,并返回映射的指针(假设操作成功)。由于底层硬件以页为单位工作,
length
会被向上取整为最接近的整页数量。
prot
参数是读写和执行权限的组合,
flags
参数至少包含
MAP_SHARED
或
MAP_PRIVATE
,还有许多其他标志可参考
mmap
手册页。
6.1 使用 mmap 分配私有内存
可以通过在
flags
参数中设置
MAP_ANONYMOUS
并将文件描述符
fd
设置为 -1,使用
mmap
分配私有内存区域。这与使用
malloc
从堆中分配内存类似,但内存是页对齐且以页为单位的倍数。内存分配在与库使用的相同区域,因此该区域也被一些人称为
mmap
区域。
匿名映射更适合大内存分配,因为它们不会使堆被大块内存固定,从而减少内存碎片化的可能性。有趣的是,
malloc
(至少在
glibc
中)对于超过 128 KiB 的请求会停止从堆中分配内存,转而使用
mmap
进行分配,所以大多数情况下,直接使用
malloc
是正确的选择,系统会自动选择最佳的分配方式。
6.2 使用 mmap 共享内存
POSIX 共享内存需要使用
mmap
访问内存段。此时,需要设置
MAP_SHARED
标志并使用
shm_open()
返回的文件描述符,示例代码如下:
int shm_fd;
char *shm_p;
shm_fd = shm_open("/myshm", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, 65536);
shm_p = mmap(NULL, 65536, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
另一个进程使用相同的调用、文件名、长度和标志来映射到该内存区域进行共享。后续可以使用
msync(2)
控制内存更新何时同步到底层文件。
6.3 使用 mmap 访问设备内存
有些驱动程序允许将其设备节点进行内存映射,从而使应用程序可以访问部分设备内存,具体实现取决于驱动程序。
6.3.1 Linux 帧缓冲器示例
以 Linux 帧缓冲器
/dev/fb0
为例,Xilinx Zynq 系列等 FPGA 也可以通过
mmap
从 Linux 进行内存访问。帧缓冲器接口在
/usr/include/linux/fb.h
中定义,包含一个
ioctl
函数用于获取显示尺寸和每像素位数。可以使用
mmap
让视频驱动程序与应用程序共享帧缓冲器,实现像素的读写操作:
int f;
int fb_size;
unsigned char *fb_mem;
f = open("/dev/fb0", O_RDWR);
/* Use ioctl FBIOGET_VSCREENINFO to find the display
dimensions and calculate fb_size */
fb_mem = mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, f, 0);
/* read and write pixels through pointer fb_mem */
6.3.2 视频 4 Linux 示例
视频 4 Linux 2(V4L2)接口在
/usr/include/linux/videodev2.h
中定义。每个视频设备都有一个节点,如
/dev/video0
等。可以使用
ioctl
函数让驱动程序分配多个视频缓冲区,并将其映射到用户空间,然后根据是播放还是捕获视频流,对这些缓冲区进行数据填充或清空操作。
7. 应用程序内存使用量的测量
和内核空间一样,用户空间内存的分配、映射和共享方式多样,使得准确回答应用程序使用了多少内存这个看似简单的问题变得困难。
7.1 使用 free 命令
可以使用
free
命令询问内核认为可用的内存量,以下是典型的输出示例:
total used free shared buffers cached
Mem: 509016 504312 4704 0 26456 363860
-/+ buffers/cache: 113996 395020
Swap: 0 0 0
乍一看,系统似乎几乎耗尽了内存,仅有 4,704 KiB 可用,占比不到 1%。但要注意,26,456 KiB 在缓冲区中,363,860 KiB 在缓存中。Linux 认为空闲内存是一种浪费,内核会使用空闲内存进行缓冲和缓存,并且知道在需要时可以缩减这些内存。去除缓冲区和缓存后,实际的空闲内存为 395,020 KiB,占总内存的 77%。使用
free
命令时,第二行
-/+ buffers/cache
后面的数字才是关键。
可以通过向
/proc/sys/vm/drop_caches
写入 1 到 3 之间的数字来强制内核释放缓存:
# echo 3 > /proc/sys/vm/drop_caches
这个数字实际上是一个位掩码,1 表示释放页缓存,2 表示释放目录项和索引节点缓存,写入 3 则同时释放这两种缓存。虽然这些缓存的确切作用在此并非关键,但重要的是要知道内核使用的这些内存可以在短时间内被回收。
free
命令只能告诉我们内存的使用情况和剩余量,无法告知哪些进程在使用不可用的内存以及使用比例。要进行更详细的测量,需要使用其他工具。
7.2 进程内存使用指标
有几个指标可以衡量进程使用的内存量,这里先介绍最容易获取的两个指标:虚拟集大小(VSS)和常驻内存大小(RSS),大多数
ps
和
top
命令的实现都支持这两个指标:
-
VSS
:在
ps
命令中称为
VSZ
,在
top
中称为
VIRT
,它是进程映射的总内存量,是
/proc/<PID>/map
中所有区域的总和。但由于虚拟内存只有部分会在任何时候实际映射到物理内存,所以这个数字的参考价值有限。
-
RSS
:后续内容会继续介绍 RSS 以及其他相关指标的详细信息。
7.2 进程内存使用指标(续)
-
RSS
:常驻内存大小(Resident Set Size),指的是进程当前实际占用的物理内存大小。它不包括那些被换出到交换空间的虚拟内存,所以能更准确地反映进程在某一时刻真正使用的内存量。在
ps命令中可以通过-o rss选项查看,在top命令中则直接显示为RES列。例如,使用ps -o pid,comm,rss可以查看所有进程的 PID、进程名和 RSS 大小。- 当系统内存紧张时,内核可能会将一些不常用的页面换出到交换空间,此时 RSS 会减小。而当进程再次访问这些被换出的页面时,会触发页错误,内核会将这些页面重新换入内存,RSS 又会增加。
-
PSS
:比例集大小(Proportional Set Size),它考虑了共享内存的情况。对于共享内存区域,每个使用该区域的进程会按比例分摊这部分内存的大小。例如,如果有 3 个进程共享了 30MB 的内存,那么每个进程的 PSS 会在其原本的内存使用基础上加上 10MB。PSS 可以更公平地反映每个进程对系统内存的实际占用情况,在分析内存使用时,特别是存在大量共享库的情况下,PSS 比 RSS 更有参考价值。可以通过
/proc/<PID>/smaps文件来查看进程的 PSS 信息。
7.3 综合分析进程内存使用
为了更全面地了解进程的内存使用情况,可以结合多个工具和指标进行分析。例如,可以使用
pmap
命令来查看进程的内存映射详细信息,包括各个内存区域的起始地址、大小、权限等。示例如下:
$ pmap -x <PID>
该命令会输出进程的详细内存映射信息,包括地址范围、权限、RSS、PSS 等。
还可以使用
valgrind
工具来检测内存泄漏和分析内存使用情况。
valgrind
可以跟踪进程的内存分配和释放操作,找出未释放的内存块,帮助开发者定位内存泄漏问题。使用示例如下:
$ valgrind --leak-check=full ./your_program
这个命令会对
your_program
进行内存泄漏检测,并输出详细的检测报告。
8. 内存优化策略
8.1 内核空间内存优化
-
精简内核配置
:根据实际需求,去除不必要的内核组件和功能。可以通过
make menuconfig或make nconfig等命令进入内核配置界面,仔细检查每个选项,只保留必要的功能。例如,如果系统不使用某些网络协议或设备驱动,可以将其禁用。 - 使用 XIP 技术 :Execute-in-Place(XIP)允许内核直接从闪存中执行代码,而不需要将整个内核镜像加载到 RAM 中,从而减少内核占用的内存。可以在编译内核时启用相关选项来支持 XIP。
-
优化 slab 分配器
:通过调整 slab 分配器的参数,减少内存碎片,提高内存使用效率。可以通过
/sys/kernel/slab目录下的相关文件来调整 slab 分配器的行为。
8.2 用户空间内存优化
-
避免内存泄漏
:在编写程序时,要确保所有分配的内存都被正确释放。可以使用智能指针(如 C++ 中的
std::unique_ptr和std::shared_ptr)来自动管理内存的生命周期,减少手动管理内存带来的风险。 -
合理使用内存分配函数
:对于小内存分配,优先使用
malloc或new;对于大内存分配,可以考虑使用mmap来避免堆内存碎片化。同时,要注意避免频繁的内存分配和释放操作,尽量复用已分配的内存。 - 优化数据结构 :选择合适的数据结构可以减少内存使用。例如,使用数组代替链表可以减少指针的开销;使用哈希表时,要合理设置哈希表的大小,避免过度分配内存。
8.3 交换空间优化
-
调整交换优先级
:可以通过修改
/etc/fstab中的swapprio参数来调整交换分区的优先级。较低的优先级可以减少不必要的交换操作,提高系统性能。例如:
/dev/zram0 none swap defaults zramsize=512M,swapprio=10
-
监控交换活动
:使用
vmstat命令可以监控系统的交换活动情况。例如,vmstat 1会每秒输出一次系统的内存、交换、CPU 等使用情况。如果发现si(从交换空间换入内存的页面数)和so(从内存换出到交换空间的页面数)的值频繁变化且数值较大,说明系统可能存在频繁的交换操作,需要进一步分析内存使用情况并进行优化。
9. 内存管理的注意事项
9.1 内核与用户空间的交互
- 页错误处理 :在用户空间程序访问未映射的内存页面时,会触发页错误。内核需要正确处理这些页错误,确保程序能够正常运行。对于小页错误,内核要快速找到可用的物理页面并进行映射;对于大页错误,要考虑文件读取的性能问题,避免影响系统的响应速度。
- 共享内存同步 :当使用共享内存进行进程间通信时,要注意内存同步问题。多个进程同时访问和修改共享内存可能会导致数据不一致的问题。可以使用同步机制,如互斥锁、信号量等,来保证数据的一致性。
9.2 内存分配的限制
-
ulimit 限制
:进程的
ulimit会限制堆和栈的最大大小。在编写程序时,要注意这些限制,避免因内存分配超过限制而导致程序崩溃。可以通过ulimit命令来查看和修改当前进程的资源限制,例如:
$ ulimit -d <new_heap_limit> # 修改堆的最大大小
$ ulimit -s <new_stack_limit> # 修改栈的最大大小
- 系统内存总量 :系统的物理内存总量是有限的,所有进程的内存使用总和不能超过这个限制。当系统内存不足时,内核会采取一些措施,如交换页面、杀死一些低优先级的进程等。因此,在设计和部署应用程序时,要充分考虑系统的内存容量,合理规划内存使用。
9.3 性能与内存的平衡
-
缓存策略
:Linux 内核会使用缓存来提高系统性能,但缓存会占用一定的内存。在内存紧张的情况下,需要权衡缓存的使用和内存的占用。可以通过调整内核参数,如
/proc/sys/vm/drop_caches来控制缓存的释放,以满足不同的性能和内存需求。 - 交换策略 :交换机制可以增加系统的可用内存,但频繁的交换操作会严重影响系统性能。要根据系统的负载和内存使用情况,合理配置交换空间的大小和优先级,避免出现磁盘抖动现象。
10. 总结
内存管理是 Linux 系统中一个复杂而重要的话题。内核空间和用户空间的内存布局、分配策略、使用情况都有各自的特点和规律。通过了解内核空间的内存布局,我们可以知道内核代码、数据、模块等是如何占用内存的;通过分析用户空间的内存分配和映射机制,我们可以更好地编写高效的应用程序。
在测量内存使用方面,虽然没有一种方法可以精确地统计内核和应用程序的内存使用量,但通过使用各种工具和指标,如
free
、
ps
、
top
、
pmap
等,我们可以尽可能接近准确地了解内存使用情况。
在优化内存使用时,要综合考虑内核和用户空间的特点,采取合适的优化策略,如精简内核配置、避免内存泄漏、合理使用内存分配函数等。同时,要注意内存管理中的各种注意事项,如页错误处理、共享内存同步、内存分配限制等,以确保系统的稳定性和性能。
通过深入理解和掌握 Linux 系统的内存管理机制,我们可以更好地优化系统资源的使用,提高系统的性能和可靠性,为各种应用场景提供更好的支持。
以下是一个简单的 mermaid 流程图,展示了内存分配和页错误处理的基本流程:
graph TD;
A[程序请求内存分配] --> B{是否有可用物理页};
B -- 是 --> C[分配物理页并映射到进程地址空间];
B -- 否 --> D[触发页错误];
D --> E{页错误类型};
E -- 小页错误 --> F[内核查找可用物理页并映射];
E -- 大页错误 --> G[内核从文件读取数据并映射物理页];
F --> H[程序继续执行];
G --> H;
C --> H;
这个流程图展示了程序请求内存分配时的基本处理流程,包括页错误的处理和不同类型页错误的处理方式。通过这个流程图,可以更直观地理解内存管理中的关键环节。
超级会员免费看

被折叠的 条评论
为什么被折叠?



