Written by: algebnaly
Date: 2024-12-09T10:56:48.000Z
这一篇博客最好能有配图, 但是我没有时间做这个
32bit系统下, 整个4GiB地址空间被划分为内核和用户空间两部分。 其中顶部的1GiB地址空间属于内核, 底部的3GiB地址空间属于用户空间。 即0xc0000000 - 0xffffffff地址空间属于内核, 0x00000000 - 0xbfffffff地址空间属于用户。 而64位系统下
Linux内核中, 内存被分为三类:
Kernel Logical Memory是一个固定的映射, 例如一个系统有512MiB的内存,且物理内存位于[0, 512 MiB], 则该内存空间将 整个物理内存映射到[PAGE_OFFSET, PAGE_OFFSET + 512MiB]范围的虚拟内存上。
kalloc()等分配内存的函数将内存映射到该区域。并非所有的物理内存都被映射到Kernel Logical Addresses上。 由于上一节所述, 内核的地址空间只有1GiB大小, 因此只有物理内存的底部被直接映射到Kernel Logical Addresses上。 而64位系统下, 内核的地址空间足够大, 可以放下所有的物理内存。 内核顶部保留了大约104MiB的内存用于非连续分配。 因此拥有Kernel Logical Addresses的物理内存为物理内存底部的大约896MiB。
Kernel Logical Addresses中的内存不会被swap掉。
也被称作vmalloc()区域。高于kernel logical address的地址空间。
该区域用于
PAGE_OFFSET以下的内存区域。
page frame是指大小为page size, 且与page size对齐的物理内存块。
用户进程请求分配内存后, 内核只是在该进程的page table中创建了相关的记录并立即返回, 而不会真正更新TLB, 只有进程访问了相应的地址时, 触发缺页异常后, 内核才会实际分配。
TLB的大小不能记录所有映射, 因此需要将映射放进页表中, 并按需要更新TLB。
当内存利用率高时, 内核可以将用户进程的内存交换到硬盘上。当用户访问到被换出的内存页时, 触发缺页异常, 内核才会将其加载到内存中, 并且有可能会被交换到其它物理内存位置上。
mmap()是从用户空间分配大量内存的标准方式。 尽管mmap()经常被用于文件, MAP_ANONYMOUS flag可以用来为该进程分配普通内存。 MAP_SHARED flag用于创建共享内存。
brk()会设置程序的break, 程序的break越高, 程序的data segment越大, 可用的内存越大。 不推荐直接使用, 因为不保证线程安全, 且不能与malloc等混用, 否则会导致未定义行为。 brk直接设置break的地址, sbrk则是增大break的值。例如sbrk(4096)就是将break增大4096字节。
malloc(), calloc()使用mmap()或brk(), 取决于需要分配的内存大小。小分配用brk(),大分配使用mmap()。 mallopt()与M_MMAP_THRESHOLD参数用于控制相关行为。
如果进程访问的内存超出了它的栈, 则会发生缺页异常, 内核会扩张程序的栈大小(物理内存不一定连续)。