读取当前linux进程内存_(笔记)Linux上的内存分配
作者: LemonNan
原文: https://juejin.im/post/5ee3c34a518825430c3ad31d
前言
本篇是對Linux內存分配的一個學習筆記.
程序內存結構
下面是在 Linux/x86-32 中典型的一個進程內存結構
文本段包含了進程運行的程序機器語言指令. 文本段具有只讀屬性, 以防止進程通過勘誤指針意外修改自身指令. 因為多個進程可以同時運行同一程序, 所以又將 文本段設為可共享 , 這樣一份程序代碼的拷貝可以映射到所有這些進程到虛擬地址空間中.
初始化數據段包含顯示初始化的全局變量和靜態變量. 程序加載到內存時, 從可執行文件中讀取這些變量的值.
未初始化數據段包含了未進行顯示初始化的全局變量和靜態變量 . (書上寫了一堆, 實際上就是懶加載)
棧(stack)是一個動態增長和收縮的段, 由棧幀(stack frames)組成 . 系統會為每個當前調用的函數分配一個棧幀.棧幀中存儲局部變量(所謂自動變量), 實參和返回值.
堆(heap)時刻在運行時(為變量)動態進行內存分配的一塊區域, 堆頂端稱做 program break .
3g(32位)以上的虛擬內存地址, 程序無法訪問.
程序的起始地址為 0x08048000 (32位)、0x00400000 (64位)
malloc 和 free
malloc
void *malloc(size_t size);棧向下增長超出之前曾達到的位置
擋在堆中分配或者釋放內存時, 通過調用 brk()、sbrk() 或 malloc 函數族來提升 program break 的位置.
調用 shmat() 連接 System V 共享內存區或者當調用 shmdt() 脫離共享內存區時.
當調用 mmap() 創建內存映射或者 munmap() 解除內存映射
特點
malloc() 返回的內存快所采用的字節對齊的方式, 在大多數的硬件架構上, 意味著 malloc 是基于 8字節 或者 16字節 邊界來分配內存的.
malloc 之后的內存, 在不使用的需要需要手動 free, malloc 和 free 一一對應, 否則可能會導致 未知錯誤(多次free) 或者 內存泄漏(沒有調用free).
允許分配小塊內存
允許隨意釋放內存快, 它們被維護于一張空閑內存列表中, 在后續內存分配調用時循環使用
free
free() 函數釋放 ptr 參數所指向的內存快
void free(void *ptr);特點
free 并不降低 program break 的位置, 而是將這塊內存添加到空閑內存列表, 供后續的 malloc() 函數循環使用.這么做有幾個原因:
被釋放的內存快通常位于堆的中間, 而非堆堆頂部, 因而降低 program break 不能達到效果
最大限度減少程序必須執行 sbrk() 調用次數(減少系統調用的開銷)
free 傳入空指針不會做任何處理(從設計上來說這不是錯誤代碼)
調用 free 后堆參數 ptr 的使用, 比如再次調用 free, 會產生錯誤并且可能導致不可預知的結果.
為什么是8/16字節對齊
CPU 讀取8字節對齊, 比如 double/long, 不對齊的話需要讀寫2次
CPU高速緩存行大小通常是 32 或者 64 字節. 如果對象是8字節對齊的數據, 則只需要占用一個緩存行, 如果不是8字節對齊的話, 則可能一部分數據在一個緩存行, 另一部分數據在其它的緩存行, 所以讀寫這個數據需要用到2個緩存行的數據而不是一個, 所有(目前1、2、3)級別的緩存都會受到此影響.
對于在磁盤中的數據, 都是以512字節為最低的單位(一個扇區的數據大小), 如果是8字節對齊的話, 則數據會被存放在一個扇區里, 可以只通過一次讀取將數據都讀取出來, 如果數據不是8字節對齊, 則 數據可能會被存放到不同的扇區中, 并且還有可能不是相鄰的扇區, 這就會 導致隨機I/O , 降低數據處理的效率, 消耗更多的硬件資源. 對于上層來說, 數據是相連的(邏輯), 但是對于底層的物理硬件來說, 數據很有可能位于不相鄰的扇區(數據處理最小單元).
so, 總結下來就是, 非對齊的數據訪問 會因為增加硬件訪問次數?比對齊的數據訪問效率低.
說起緩存行, Java中有一些框架(比如Disruptor)考慮到了不同的CPU架構, 使用了CPU支持的緩存行填充, 以防止 偽共享(這里暫不做過多描述)?的發生從而降低效率.
通過 sysctl -a 查看
# 我的電腦中的數據hw.cachelinesize: 64hw.l1icachesize: 32768hw.l1dcachesize: 32768hw.l2cachesize: 262144hw.l3cachesize: 3145728虛擬內存管理
內核為每一個進程都維護一張頁表(page table) , 頁表中的每個條目要么指出一個虛擬頁面在 RAM 中的所在位置, 要么表明其當前駐留在磁盤上, 若進程訪問的地址并無頁表條目與之對應, 進程將會收到一個 SIGSEGV 信號.
Q: 虛擬頁面的數據為什么會在磁盤上?
A: 每個程序中只有一部分 page 會駐留在 物理內存(RAM) 中, 未使用的 page 會被拷貝保存到交換區(swap area)內, 這是磁盤空間中的保留區域, 作為 RAM 的補充, 只有在需要的時候才會載入 物理內存.
進程在讀取的時候, 如果訪問的頁面沒有駐留在物理內存中, 將會發生頁面錯誤(page fault), 內核即刻掛起的執行, 同時從磁盤中將該頁面載入內存.
在 x86-32 中, page size 為 4096 字節(4KB), 一些其它的Linux使用的頁面比 4096 字節更大.
Alpha 使用的 page size = 8192 字節(8KB), IA-64 的page size是可以改變的, 默認為 16384 字節.程序通過調用 sysconf(_SC_PAGESIZE) 獲取系統虛擬內存的 page size.
虛擬內存的實現需要硬件中分頁內存管理單元(PMMU)的支持, PMMU 把要訪問的每個虛擬內存地址轉換成相應的物理內存地址, 當特定虛擬內存地址所對應的頁沒有駐留于 RAM 中時, 將以頁面錯誤(page fault)通知內核.
有效虛擬內存范圍
由于 內核能為進程分配和釋放頁(和頁表條目) , 所以進程的有效虛擬地址范圍在其生命周期中可以發生變化. 如下場景會導致范圍變化:
棧向下增長超出之前曾達到的位置
擋在堆中分配或者釋放內存時, 通過調用 brk()、sbrk() 或 malloc 函數族來提升 program break 的位置.
調用 shmat() 連接 System V 共享內存區或者當調用 shmdt() 脫離共享內存區時.
當調用 mmap() 創建內存映射或者 munmap() 解除內存映射
局部性原理
在計算機中大多數程序都有一個共同特點, 訪問局部性 .
訪問局部性包含兩方面:
空間局部性: 程序傾向于訪問在最近訪問過的內存地址附近的內存(由于指令是順序執行的, 并且有時會按順序處理數據結構)
時間局部性: 這意味著數據被訪問到, 在之后較短的時間內會被再次訪問到(可能是由于循環)
優點
虛擬內存使得進程的虛擬地址空間和RAM的物理地址空間隔離開, 有以下一些好處
進程與進程、進程與內核相互隔離, 所以一個進程不能讀取其它進程或內核的內存, 因為每個進程的頁表條目指向截然不同的物理內存地址.
適當情況下, 多個進程鞥狗共享內存. 因為不同的進程頁表條目可以指向相同的物理內存(RAM)地址.通常發生在如下的場景:
執行同一程序的多個進程, 共享一份程序代碼副本. 當多個進程執行相同的程序文件(或加載相同的共享庫), 會隱式實現這一類型的共享.
進程通過 shmget() 和 mmap() 系統調用顯示請求與其它進程共享內存, 這樣的目的是為了進程間的通信.
實現保護機制: 相同的內存, 不同的進程可以設置不同的訪問權限, 某些進程只讀、某些擁有所有權限等.
因為需要駐留在內存中的僅是程序的一部分, 所以程序的加載和運行都變快了, 而且一個程序所占用的大小(虛擬內存) 能夠超出 RAM 容量.(因為有的事通過虛擬內存管理存放到了磁盤上)
一個進程所使用的RAM減少了, RAM中同時可容納的進程數量增多. 這樣的話加大了在任一時刻CPU可執行至少一個進程的概率, 這樣往往也會提高CPU的利用率.
總結
以上是生活随笔為你收集整理的读取当前linux进程内存_(笔记)Linux上的内存分配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 键盘无响应-如何修复键盘注册表
- 下一篇: 果然还是十三香!iPhone 13是Q4