加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

【內(nèi)存管理】內(nèi)存布局介紹

07/02 14:42
1246
閱讀需 33 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

ARM32位系統(tǒng)的內(nèi)存布局圖

32位操作系統(tǒng)的內(nèi)存布局很經(jīng)典,很多書籍都是以32位系統(tǒng)為例子去講解的。32位的系統(tǒng)可訪問的地址空間為4GB,用戶空間為1GB ~ 3GB,內(nèi)核空間為3GB ~ 4GB。

為什么要劃分為用戶空間和內(nèi)核空間呢?

一般處理器會把運行模式分為好幾個,比如x86分為rang0 ~ rang3級別。ARMv7架構(gòu)中,又分為好幾個模式,比如svc模式是給內(nèi)核用的,usr模式是給用戶態(tài)使用的。

當(dāng)一個進程執(zhí)行系統(tǒng)調(diào)用時,會陷入到內(nèi)核態(tài)中,這個時候運行模式就從usr模式轉(zhuǎn)換為svc模式,這就是我們常說的內(nèi)核態(tài)。處于內(nèi)核態(tài)的進程是可以訪問內(nèi)核空間的。所以就根據(jù)CPU的運行模式劃分了兩個空間。

我們先看下1GB的內(nèi)核空間是怎么劃分的,32位的系統(tǒng)中,通常配置的物理內(nèi)存通常是大于1GB的,所以物理內(nèi)存會劃分為兩部分,低端內(nèi)存稱為線性映射區(qū),高端內(nèi)存稱為高端映射區(qū)。那這個分界線是怎么計算的呢,在ARM32中,分界線為760M。低端內(nèi)存會做一比一映射到3GB ~ 3GB+760M。

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

高端內(nèi)存的映射就沒有線性映射那么簡單了,使用高端內(nèi)存時需要完成動態(tài)映射。

我們先看下1GB的內(nèi)核空間剩下都做什么使用了。

vmalloc區(qū)域:分配的內(nèi)存在虛擬地址是連續(xù)的,物理頁面可以是離散的。vmalloc大概占用了200M物理內(nèi)存。

fixmap:Fix map中的fix指的是固定的意思,那么固定什么東西呢?其實就是虛擬地址是固定的,也就是說,有些虛擬地址在編譯(compile-time)的時候就固定下來了,而這些虛擬地址對應(yīng)的物理地址不是固定的,是在kernel啟動過程中被確定的。

vector:vector區(qū)域用于映射CPU vector page,大小一頁4KB,從0xffff0000 - 0xffff1000。

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

堆空間:從進程的開始到1GB的這部分我們稱為堆空間,這部分主要是給malloc使用的。

mmap空間:1GB到3GB這部分是給mmap空間使用的,mmap可以用來映射文件也可以映射匿名頁面。通常用戶態(tài)分配大段內(nèi)存的時候,Linux通常會使用mmap來完成分配。

從進程的角度看內(nèi)存布局

readelf 查看程序段

接下來,我們通過一個C語言程序?qū)W習(xí)下內(nèi)存布局,這個例子很簡單,用malloc函數(shù)分配了內(nèi)存內(nèi)存,然后使用memset將該區(qū)域清零。

使用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中流行的可執(zhí)行文件的格式就是elf。使用gcc編譯的elf就是我們講的elf文件,目標(biāo)文件除了包含了編譯后的機器指令代碼,還包含其他鏈接信息,比如符號表,調(diào)試信息,字符串等,通常這些信息會根據(jù)不同的屬性存放在不同的段(section)中,這里我們只關(guān)注常見的段 。

.init:程序初始化的代碼段。

.text:代碼段,程序編譯完后的機器指令。

.data:初始化過的全局的靜態(tài)變量,還有一些局部的靜態(tài)變量。

.rodata:只讀變量,字符串,常量等。

.bss:未初始化的全局變量以及初始化為零的變量。

readelf 查看程序頭

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

之前我們看到的30個段,在這里分成了7個族,并且顯示每個族都包含那些段,這里我們只關(guān)注叫l(wèi)oad的族,其他族主要是在程序裝載的時候起到輔助作用。

第一個族里面包含init,text段,他的執(zhí)行權(quán)限是只讀,可執(zhí)行的(RE)。起始地址0x0000000000400000,大小是0x00000000000b5986。

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

進程映射的過程

地址:本段在虛擬內(nèi)存中的地址范圍;對應(yīng)vm_area_struct中的vm_startvm_end。

權(quán)限:本段的權(quán)限; r-讀,w-寫,x-執(zhí)行, p-私有;對應(yīng)vm_flags。

偏移地址:即本段映射地址在文件中的偏移;對于有名映射指本段映射地址在文件中的偏移,對應(yīng)vm_pgoff;對于匿名映射為vm_area_struct->vm_start。

主設(shè)備號與次設(shè)備號:所映射的文件所屬設(shè)備的設(shè)備號,對應(yīng)vm_file->f_dentry->d_inode->i_sb->s_dev。匿名映射為0。其中fd為主設(shè)備號,00為次設(shè)備號。

文件索引節(jié)點號:對應(yīng)vm_file->f_dentry->d_inode->i_ino,與ls –i顯示的內(nèi)容相符。匿名映射為0。

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

smaps 可以查看更多的內(nèi)容

???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個物理內(nèi)存,但我們的測試程序只分配了100k物理內(nèi)存,這里匿名頁面比分配的要大,這是因為進程在裝載的時候也要消耗一些匿名頁面。

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?

根據(jù)以上信息,可以繪制出測試程序內(nèi)存的布局圖。

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

數(shù)據(jù)段的VMA屬于匿名映射,bss,data段具有相同的權(quán)限,在OS加載時,會映射到數(shù)據(jù)段的VMA中。

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

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

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

64位系統(tǒng)的布局圖

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

大家可以看這張圖,把空間分為了三部分,一部分是內(nèi)核空間,一部分是非規(guī)范區(qū)域(大家都不使用的),最后是用戶空間。

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

非規(guī)范區(qū)域

內(nèi)核空間:0xffff_0000_0000_0000到0xffff_ffff_ffff_ffff。一共有256TB。

內(nèi)核空間又做了如下細分:

vmalloc區(qū)域:vmalloc函數(shù)使用的虛擬地址空間,kernel image也在vmalloc區(qū)域,內(nèi)核鏡像的起始地址 = KIMAGE_ADDR + TEXT_OFFSET, TEXT_OFFSET是內(nèi)存中的內(nèi)核鏡像相對內(nèi)存起始位置的偏移。

vmemmap區(qū)域:內(nèi)存的物理地址如果不連續(xù)的話,就會存在內(nèi)存空洞(稀疏內(nèi)存),vmemmap就用來存放稀疏內(nèi)存的page結(jié)構(gòu)體的數(shù)據(jù)的虛擬地址空間。

PCI I/O區(qū)域:pci設(shè)備的I/O地址空間

Modules區(qū)域:內(nèi)核模塊使用的虛擬地址空間

normal memory線性映射區(qū):范圍是【0xffff_8000_0000_0000, 0xffff_ffff_ffff_ffff】, 一共有128TB, 但這里代碼對應(yīng)的是memblock_start_of_DRAM()memblock_end_of_DRAM()函數(shù)。memory根據(jù)實際物理內(nèi)存大小做了限制,所以memroy顯示了實際能夠訪問的內(nèi)存區(qū)。

MLM(__phys_to_virt(memblock_start_of_DRAM()),?(unsigned?long)high_memory))
high_memory?=?__va(memblock_end_of_DRAM()?-?1)?+?1;

最終是通過dts或acpi中配置的memory節(jié)點確定的。

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
KSZ8863RLLI 1 Microchip Technology Inc DATACOM, LAN SWITCHING CIRCUIT, PQFP48

ECAD模型

下載ECAD模型
$5.09 查看
AD73311ARSZ 1 Analog Devices Inc Single-Channel, 3 V and 5 V Front-End Processor for General Purpose Applications Including Speech and Telephony

ECAD模型

下載ECAD模型
$10.72 查看
KSZ8081MNXIA-TR 1 Microchip Technology Inc DATACOM, ETHERNET TRANSCEIVER, QCC32

ECAD模型

下載ECAD模型
$1.71 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

作者就職于某500強公司,擔(dān)任BSP工程師。具有豐富的嵌入式開發(fā)經(jīng)驗。專欄主要分享計算機基礎(chǔ),操作系統(tǒng),Linux驅(qū)動開發(fā),Arm體系與架構(gòu),C/C++,數(shù)據(jù)結(jié)構(gòu)與算法等相關(guān)文章。歡迎關(guān)注我的公眾號【嵌入式與Linux那些事】,一起學(xué)習(xí)交流。