Linux内存管理笔记

Written by: algebnaly

Date: 2024-12-09T10:56:48.000Z

这一篇博客最好能有配图, 但是我没有时间做这个

虚拟内存

32bit系统下, 整个4GiB地址空间被划分为内核和用户空间两部分。 其中顶部的1GiB地址空间属于内核, 底部的3GiB地址空间属于用户空间。 即0xc0000000 - 0xffffffff地址空间属于内核, 0x00000000 - 0xbfffffff地址空间属于用户。 而64位系统下

Linux Kernel中内存空间的分类

Linux内核中, 内存被分为三类:

Kernel Logical Addresses

在于32位且小于大约1GiB内存的系统上

Kernel Logical Memory是一个固定的映射, 例如一个系统有512MiB的内存,且物理内存位于[0, 512 MiB], 则该内存空间将 整个物理内存映射到[PAGE_OFFSET, PAGE_OFFSET + 512MiB]范围的虚拟内存上。

在于32位且大于1GiB内存的系统上

并非所有的物理内存都被映射到Kernel Logical Addresses上。 由于上一节所述, 内核的地址空间只有1GiB大小, 因此只有物理内存的底部被直接映射到Kernel Logical Addresses上。 而64位系统下, 内核的地址空间足够大, 可以放下所有的物理内存。 内核顶部保留了大约104MiB的内存用于非连续分配。 因此拥有Kernel Logical Addresses的物理内存为物理内存底部的大约896MiB。

Kernel Logical Addresses中的内存不会被swap掉。

Kernel Virtual Addresses

也被称作vmalloc()区域。高于kernel logical address的地址空间。

该区域用于

User Virtual Addresses

PAGE_OFFSET以下的内存区域。

MMU

page frame是指大小为page size, 且与page size对齐的物理内存块。

Lazy Allocation

用户进程请求分配内存后, 内核只是在该进程的page table中创建了相关的记录并立即返回, 而不会真正更新TLB, 只有进程访问了相应的地址时, 触发缺页异常后, 内核才会实际分配。

Page table

TLB的大小不能记录所有映射, 因此需要将映射放进页表中, 并按需要更新TLB。

swapping

当内存利用率高时, 内核可以将用户进程的内存交换到硬盘上。当用户访问到被换出的内存页时, 触发缺页异常, 内核才会将其加载到内存中, 并且有可能会被交换到其它物理内存位置上。

User Space

mmap()是从用户空间分配大量内存的标准方式。 尽管mmap()经常被用于文件, MAP_ANONYMOUS flag可以用来为该进程分配普通内存。 MAP_SHARED flag用于创建共享内存。

brk()/sbrk()

brk()会设置程序的break, 程序的break越高, 程序的data segment越大, 可用的内存越大。 不推荐直接使用, 因为不保证线程安全, 且不能与malloc等混用, 否则会导致未定义行为。 brk直接设置break的地址, sbrk则是增大break的值。例如sbrk(4096)就是将break增大4096字节。

关于高层的分配接口

malloc(), calloc()使用mmap()或brk(), 取决于需要分配的内存大小。小分配用brk(),大分配使用mmap()。 mallopt()与M_MMAP_THRESHOLD参数用于控制相关行为。

如果进程访问的内存超出了它的栈, 则会发生缺页异常, 内核会扩张程序的栈大小(物理内存不一定连续)。

参考资料