內存地址在0x7ff16473d000,相當於140,674,749,157,376(127T965GB(131013GB)處開始,47位最大是128TB,131072GB),如下,也就是在用戶空間(0~0x7FFF FFFF FFFF,128GB)快頂部(差59GB)的位置。
因為48bit空間也要滿足“兩頭頂格”的習慣,整個可用地址范圍變成了0~0x7FFF FFFF FFFF和0x8000 0000 0000~0xFFFF FFFF FFFF兩個不連續的地址空間上的的幾個更加離散的小島。以首位區分或者理解為正負符號,Linux Kernel使用“1”作為系統地址空間,使用“0”作為用戶地址空間(小於47bit可分配給用戶空間)。
雖然段從低到高分配,棧從高到低分配,本質上都是一樣的,就像數組從頭分配還是從尾分配,存儲上都是從前往后,從效率角度思考的優化結論,所以指針操作都是++為主。
上右圖共享內存段中還包含了動態鏈接庫。其二,有些地方寫着共享內存段是從下往上、有些是從上往下(如上右圖,實際也是如此,見下文pg共享內存段分配),但肯定有個基址(如上mmap_base),從實際角度來看,從上往下更合適,為什么?因為棧空間通過內核最大進程數就能估計出來,堆大小估計不出來,避免堆申請越界,所以從上往下是更合適的。
對於linux程序而言,有一個非常重要的可執行文件格式ELF(Executable and Linkable Format),它是對象文件、可執行文件、庫文件、core dump文件的格式。位於用戶空間的底部,通常在啟動時就確定並且不變。其組成部分從下到上為:
對可執行文件而言,主要有4部分:.text, .data, .rodata和.bss(未初始化的數據),readelf -S execname可以查看每部分的相對位置。data+bss+heap的大小由RLIMIT_DATA控制最大值。棧大小由RLIMIT_STACK控制。
(1)用戶空間:0x0000_0000_0000_0000到0x0000_ffff_ffff_ffff,一共有256TB。一般只用128TB,所以只會到0x7fff ffff ffff。
(2)非規范區域
(3)內核空間:0xffff_0000_0000_0000到0xffff_ffff_ffff_ffff。一共有256TB。一般只用128TB。應用編程不可見。
內核空間又做了如下細分:
- vmalloc區域:0xffff_0000_0000_0000到0xffff_7bff_bfff_0000,大小為126974GB。
- vmemmap區域:0xffff_7bff_c000_0000到0xffff_7fff_c000_0000,大小為4096GB。
- PCI I/O區域:0xffff_7fff_ae00_0000到0xffff_7fff_be00_0000,大小為16MB。
- Modules區域:0xffff_7fff_c000_0000到0xffff_8000_0000_0000,大小為64MB。
- normal memory線性映射區:0xffff_8000_0000_0000到0xffff_ffff_ffff_ffff,大小為128TB。主要從這里開始。
所以更准確的64為內存划分如下:
綠色是用戶區,黃色是內核態。
用戶區域各部分的操作
ELF各個部分的操作通常如下:
注:上圖mmap並不是那么准,僅表示在堆之上。
由於棧通常比較小,所以mmap也是往上漲的。
在linux內存監控中,通常給系統管理員可見的是free/used/buffered/cached,如下:
結合上面兩張圖也可知,pagecache是屬於內核管理的。所以堆、棧、以及程序靜態數據是主要的used部分。
mmap一般來說一定在pagecache中,但是pagecache可能不一定只有mmap實現。所以mmap映射在pagecache中,如下:
交換區也是由頁表管理的。
從下可知,heap是從低到高分配,共享內存從高到低。
0x7f開頭的都是共享內存塊或其中的某個變量。
程序代碼段、數據段映射到可執行文件的物理實現如下:
每個程序都有起始地址、各個端的offset,重定位之后就得到運行時的絕對地址了,通過readelf可以看出。如下:
雖然大多數進程不會訪問同一物理內存,但是也是可以的,例如進程間通信常用的共享內存技術,使用共享內存段、mmap等情況,比如postgresql/oracle里面的多進程架構,相同進程會訪問大量相同shared buffer塊,所以top rss加起來比物理內存大也正常。如下:
在任何一個進程中,都完全享有自己的內存地址空間,雖然在物理上可能完全是離散的。由於64位地址足夠大,基本不太可能出現虛擬內存oom的情況,只能是物理內存不足或pagecache swapout來不及。
內存子系統如何確定malloc、mmap、shm、程序啟動地址內存的地址
待續。。
虛擬地址到物理地址的轉換
待續
共享內存段在linux內存中的存放位置
共享內存段(包括sys v實現和poxis實現)位於棧和堆之間。
參考:https://zhuanlan.zhihu.com/p/81399122
后補:https://xie.infoq.cn/article/8f738c08e409b011e92acb03f 【精通內核】計算機程序的本質、內存組成與 ELF 格式
linux內存使用監控與管理 https://www.cnblogs.com/zhjh256/p/15424236.html