【內存管理】內存佈局介紹

ARM32 位系統的內存佈局圖

32 位操作系統的內存佈局很經典,很多書籍都是以 32 位系統爲例子去講解的。32 位的系統可訪問的地址空間爲 4GB,用戶空間爲 1GB ~ 3GB,內核空間爲 3GB ~ 4GB。

爲什麼要劃分爲用戶空間和內核空間呢?

一般處理器會把運行模式分爲好幾個,比如 x86 分爲 rang0 ~ rang3 級別。ARMv7 架構中,又分爲好幾個模式,比如 svc 模式是給內核用的,usr 模式是給用戶態使用的。

當一個進程執行系統調用時,會陷入到內核態中,這個時候運行模式就從 usr 模式轉換爲 svc 模式,這就是我們常說的內核態。處於內核態的進程是可以訪問內核空間的。所以就根據 CPU 的運行模式劃分了兩個空間。

我們先看下 1GB 的內核空間是怎麼劃分的,32 位的系統中,通常配置的物理內存通常是大於 1GB 的,所以物理內存會劃分爲兩部分,低端內存稱爲線性映射區,高端內存稱爲高端映射區。那這個分界線是怎麼計算的呢,在 ARM32 中,分界線爲 760M。低端內存會做一比一映射到 3GB ~ 3GB+760M。

這裏講的線性映射就是直接把物理內存的地址映射到線性映射區中,假設物理內存的 DDR 起始地址是 0,映射的時候就有一個偏移量,這個偏移量就是 0XC0000000,page offset。線性映射的地址我們就可以很方便的完成虛擬地址到物理地址的轉換,只需要加減一個 offset 就可以。

高端內存的映射就沒有線性映射那麼簡單了,使用高端內存時需要完成動態映射。

我們先看下 1GB 的內核空間剩下都做什麼使用了。

接下來看下 3GB 用戶空間的劃分方式,一個進程要運行起來,必然要有自己的代碼段和數據段,這部分在加載的時候就會被映射到虛擬地址。

從進程的角度看內存佈局

readelf 查看程序段

接下來,我們通過一個 C 語言程序學習下內存佈局,這個例子很簡單,用 malloc 函數分配了內存內存,然後使用 memset 將該區域清零。

使用 gcc 編譯爲 elf 後,可以使用 readelf 查看該程序包含那些段。

#include <stdio.h>
#include <string.h> 
#include <stdlib.h>

#define SIZE (100 * 1024)
void main()
{
    char* buf = malloc(SIZE);
    memset(buf, 0x58, SIZE);
    while(1)
        sleep(10000);
}
gcc -static  memory_process.c -o memory_process.elf

我們知道,通常 Linux 中流行的可執行文件的格式就是 elf。使用 gcc 編譯的 elf 就是我們講的 elf 文件,目標文件除了包含了編譯後的機器指令代碼,還包含其他鏈接信息,比如符號表,調試信息,字符串等,通常這些信息會根據不同的屬性存放在不同的段(section)中,這裏我們只關注常見的段 。

readelf 查看程序頭

使用 - l 參數讀下程序頭(program header),它是用來描述 OS 是如何被映射到進程的虛擬地址空間的。

之前我們看到的 30 個段,在這裏分成了 7 個族,並且顯示每個族都包含那些段,這裏我們只關注叫 load 的族,其他族主要是在程序裝載的時候起到輔助作用。

第一個族裏麪包含 init,text 段,他的執行權限是隻讀,可執行的(RE)。起始地址0x0000000000400000,大小是0x00000000000b5986

另外一個族主要包含 data 和 bss 段,他的執行權限是可讀寫(RW)。起始地址0x00000000006b6120,大小是0x00000000000051b8

進程映射的過程

  1. 地址:本段在虛擬內存中的地址範圍;對應vm_area_struct中的vm_startvm_end

  2. 權限:本段的權限; r - 讀,w - 寫,x - 執行, p - 私有; 對應 vm_flags。

  3. 偏移地址:即本段映射地址在文件中的偏移;對於有名映射指本段映射地址在文件中的偏移, 對應vm_pgoff;對於匿名映射爲vm_area_struct->vm_start

  4. 主設備號與次設備號:所映射的文件所屬設備的設備號,對應vm_file->f_dentry->d_inode->i_sb->s_dev。匿名映射爲 0。其中 fd 爲主設備號,00 爲次設備號。

  5. 文件索引節點號:對應vm_file->f_dentry->d_inode->i_ino,與 ls –i 顯示的內容相符。匿名映射爲 0。

  6. 映射的文件名:對有名映射而言,是映射的文件名,對匿名映射來說,是此段內存在進程中的作用。[stack] 表示本段內存作爲棧來使用,[heap] 作爲堆來使用,其他情況則爲無。

smaps 可以查看更多的內容

➜  example cat /proc/5823/smaps  
00400000-004b6000 r-xp 00000000 08:01 2319863                            /home/zhongyi/code/example/memory_process.elf
Size:                728 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                 640 kB
Pss:                 640 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:       640 kB
Private_Dirty:         0 kB
Referenced:          640 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd ex mr mw me dw sd 
006b6000-006bc000 rw-p 000b6000 08:01 2319863                            /home/zhongyi/code/example/memory_process.elf
Size:                 24 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                  24 kB
Pss:                  24 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         8 kB
Private_Dirty:        16 kB
Referenced:           24 kB
Anonymous:            16 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd wr mr mw me dw ac sd 
006bc000-006bd000 rw-p 00000000 00:00 0 
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd wr mr mw me ac sd 
010cc000-010ef000 rw-p 00000000 00:00 0                                  [heap]
Size:                140 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                 108 kB
Pss:                 108 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:       108 kB
Referenced:          108 kB
Anonymous:           108 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd wr mr mw me ac sd 
7ffd5e0db000-7ffd5e0fc000 rw-p 00000000 00:00 0                          [stack]
Size:                132 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                  16 kB
Pss:                  16 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:        16 kB
Referenced:           16 kB
Anonymous:            16 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd wr mr mw me gd ac 
7ffd5e100000-7ffd5e103000 r--p 00000000 00:00 0                          [vvar]
Size:                 12 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   0 kB
Pss:                   0 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            0 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd mr pf io de dd sd 
7ffd5e103000-7ffd5e105000 r-xp 00000000 00:00 0                          [vdso]
Size:                  8 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   0 kB
Shared_Clean:          4 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            4 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd ex mr mw me de sd 
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   0 kB
Pss:                   0 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            0 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: ex

堆裏面,匿名頁面分配了 108 個物理內存,但我們的測試程序只分配了 100k 物理內存,這裏匿名頁面比分配的要大,這是因爲進程在裝載的時候也要消耗一些匿名頁面。

010cc000-010ef000 rw-p 00000000 00:00 0                                  [heap]
Size:                140 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                 108 kB
Pss:                 108 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:       108 kB
Referenced:          108 kB
Anonymous:           108 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:            0
VmFlags: rd wr mr mw me ac sd

根據以上信息,可以繪製出測試程序內存的佈局圖。

測試程序進程的 elf 這裏只列出了常用的段。代碼段的 VMA 屬於 page cache 映射,這裏把 init 段,text 段,rodata 段分爲一個族,因爲他們具有相同的權限,在進程加載的時候,會映射到代碼段的 VMA 中。

數據段的 VMA 屬於匿名映射,bss,data 段具有相同的權限,在 OS 加載時,會映射到數據段的 VMA 中。

從數據段開始的地方就屬於堆空間,我們在程序中用 malloc 分配了 100K 空間,這 100K 大小,也是在堆空間有對應的位置存在。

另外就是棧的 VMA,進程有屬於自己的 VMA 的棧。

以上就介紹了進程的 ELF 如何和進程的地址空間映射起來的。

64 位系統的佈局圖

64 位系統可以訪問的空間就變得很大了。不過是 ARM 還是 X86,實際的物理地址都不會用到 64 根地址線,通常是使用了 48 根地址線。而且,劃分的用戶空間和內核空間都是非常大的。

大家可以看這張圖,把空間分爲了三部分,一部分是內核空間,一部分是非規範區域(大家都不使用的),最後是用戶空間。

  1. 用戶空間:0x0000_0000_0000_0000 到 0x0000_ffff_ffff_ffff,一共有 256TB。

  2. 非規範區域

  3. 內核空間:0xffff_0000_0000_0000 到 0xffff_ffff_ffff_ffff。一共有 256TB。

內核空間又做了如下細分:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ENysyY_YWv7Nf15H2J93aA