程式設計師的自我修養 chapter 10 記憶體

30
程式設計師的自我修養 Chapter 10 記憶體 Shu-Yu Fu ([email protected])

Upload: shu-yu-fu

Post on 14-Dec-2014

1.635 views

Category:

Documents


7 download

DESCRIPTION

 

TRANSCRIPT

Page 1: 程式設計師的自我修養 Chapter 10 記憶體

程式設計師的自我修養

Chapter 10 記憶體

Shu-Yu Fu ([email protected])

Page 2: 程式設計師的自我修養 Chapter 10 記憶體

本章概述

本章討論程式執行時Stack和heap的配置!● 藉由觀察stack的變化導出Calling convention,

然後函式如何傳遞返回值!● 除了比較heap在Linux和Windows上的實作外,

我們還會討論heap管理演算法!● PS︰記得參考這篇產生你的assembly code+C

codeHow to get gcc to interleave assembly output with original source codeUsing GCC to produce readable assembly?

Page 3: 程式設計師的自我修養 Chapter 10 記憶體

程式的記憶體配置

Flat memory model or linear memory model refers to a memory addressing

paradigm in low-level software design such that the CPU can directly (and sequentially/linearly) address all of the available memory locations without having to resort to any sort of memory segmentation or paging schemes.int *p = (int *)0x12345678;++*p;

● 記憶體空間"預設"的區域︰○ 堆疊﹙stack﹚○ 堆積﹙heap﹚○ ELF / dynamic libraries / reserved

Page 4: 程式設計師的自我修養 Chapter 10 記憶體

程式的記憶體配置

Flat memory model or linear memory model refers to a memory addressing

paradigm in low-level software design such that the CPU can directly (and sequentially/linearly) address all of the available memory locations without having to resort to any sort of memory segmentation or paging schemes.int *p = (int *)0x12345678;++*p;

● 記憶體空間"預設"的區域︰○ 堆疊﹙stack﹚○ 堆積﹙heap﹚○ ELF / dynamic libraries / reserved

Page 5: 程式設計師的自我修養 Chapter 10 記憶體

堆疊與呼叫慣例

● 在i386下,esp暫存器指向堆疊頂端● 堆疊保存了一個函式呼叫所需要的維護資訊,

常被稱為堆疊框架﹙stack frame﹚或活動記

錄﹙activate record﹚○ 函式的傳回位址和參數○ 臨時變數○ 保存的上下脈絡

● ESP暫存器始終指向堆疊頂部,EBP﹙又稱為

框架指標Frame Pointer﹚指向函式活動記錄的一個固定位置。

Page 6: 程式設計師的自我修養 Chapter 10 記憶體

堆疊與呼叫慣例

● 在i386下,esp暫存器指向堆疊頂端● 堆疊保存了一個函式呼叫所需要的維護資訊,

常被稱為堆疊框架﹙stack frame﹚或活動記

錄﹙activate record﹚○ 函式的傳回位址和參數○ 臨時變數○ 保存的上下脈絡

● ESP暫存器始終指向堆疊頂部,EBP﹙又稱為

框架指標Frame Pointer﹚指向函式活動記錄的一個固定位置。

Page 7: 程式設計師的自我修養 Chapter 10 記憶體

呼叫一個函式

1. 把所有或一部分參數推入堆疊中,如果有其他參數沒有入堆疊,那麼使用某些特定的暫存器傳遞

2. 把當前指令的下一條指令的位址推入堆疊中3. 跳轉到函式體執行

Page 8: 程式設計師的自我修養 Chapter 10 記憶體

呼叫一個函式

1. 把所有或一部分參數推入堆疊中,如果有其他參數沒有入堆疊,那麼使用某些特定的暫存器傳遞

2. 把當前指令的下一條指令的位址推入堆疊中3. 跳轉到函式體執行

Page 9: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

EBP

HIGH LOW

ESP

Page 10: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

EBP

HIGH LOW

EBP

ESP

Page 11: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

HIGH LOW

EBP

ESP

EBP

Page 12: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

HIGH LOW

EBP

ESP

VAR

VAR

VAR

EBP

Page 13: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

HIGH LOW

EBP

VAR

VAR

VAR

REG

REG

ESP

EBP

Page 14: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

HIGH LOW

EBP

ESP

VAR

VAR

VAR

EBP

Page 15: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

HIGH LOW

EBP

ESP

VAR

VAR

VAR

EBP

Page 16: 程式設計師的自我修養 Chapter 10 記憶體

函式的開始與結束

EBP

HIGH LOW

ESP

VAR

VAR

VAR

Page 17: 程式設計師的自我修養 Chapter 10 記憶體

小知識

● 未初始化的區域變數的初始化成0xcccccccc● Hot Patch Prologue

More: 3.13 Hotpatch Support在GCC對應的實作為ms_hook_prologue

Page 18: 程式設計師的自我修養 Chapter 10 記憶體

呼叫慣例

int foo(int n, float m){ int a = 0, b = 0; ...}

● 先推入n後推入m?● 呼叫方和被呼叫方對於呼叫的約定

○ 參數的傳遞順序﹙左先?右先?﹚和方式﹙堆疊?暫存器?﹚

○ 堆疊的維護方式,由呼叫方或被呼叫方維護○ 名稱修飾﹙name-mangling﹚的策略,對呼叫慣例進

行區分

Page 19: 程式設計師的自我修養 Chapter 10 記憶體

列表

呼叫慣例 出堆疊方 參數傳遞 名稱修飾

cdecl 函式呼叫方 從右至左的順序推參數入堆疊

底線+函式名

stdcall 函式本身 從右至左的順序推參數入堆疊

底線+函式名+@+參數的位元組數,如函式int func(int a, double b)的修飾名是_func@12

fastcall 函式本身 頭兩個DWORD(4位元組)類型或者占更少位元組的參數被放入暫存器,其他剩下的參數按右到左的順序推入堆疊

@+函式名+@+參數的位元組數

pascal 函式本身 從左至右的順序推參數入堆疊

較為複雜,參見pascal文件

Page 20: 程式設計師的自我修養 Chapter 10 記憶體

更多的呼叫慣例

● 用於特殊場合naked call● C++更複雜的名稱修飾策略和thiscall

○ VC,this存放於ecx暫存器○ GCC,this是函式的第一個參數

Page 21: 程式設計師的自我修養 Chapter 10 記憶體

函式傳回值傳遞

● eax是傳遞傳回值的通道● 大於4-byte的傳回值如何傳遞?

○ 5-byte ~ 8-byte採eax(low-byte)和edx(high-byte)聯合傳回

○ > 8-byte,用隱含參數+eax

● 可以不懂過程,但,要記得不要輕易傳回大尺寸的物件

● C++提出了傳回值最佳化﹙Return Value Optimization, RVO﹚,可以將某些場合下的物件複製減少1次

Page 22: 程式設計師的自我修養 Chapter 10 記憶體

堆積與記憶體管理

● 為什麼需要堆積?○ 動態產生○ 堆疊上的資料在函式傳回時就會被釋放,那main()裡的

區域變數呢?

● 把配置和釋放做在作業系統核心○ 每次都要系統呼叫,效能太差

● 應用程式自行管理,多數的情況是執行階段程式庫管理

○ 配置○ 管理,堆積的分配演算法

Page 23: 程式設計師的自我修養 Chapter 10 記憶體

Linux行程堆積管理

● brk系統呼叫,設定行程資料區段的結束位址int brk(void * end_data_segment);

● mmap系統呼叫,申請一段虛擬位址空間void * mmap(void * start, // 起始位址

size_t length, // 長度 int prot, // 權限﹙讀/寫/執行﹚

int flags, // 類型﹙檔案/匿名﹚

int fd, // fd...XD

off_t offset); // 偏移量

● malloc一次能夠申請的最大空間是多少?○ Demo

Page 24: 程式設計師的自我修養 Chapter 10 記憶體

Windows行程堆積管理

● VirtualAlloc()用來向系統申請空間,應用程式可以按照需要隨意使用。

● 堆積管理器﹙Heap Manager﹚實作了空間分配演算法。○ NTDLL.DLL︰HeapCreate﹑HeapAlloc﹑HeapFree﹑HeapDestroy

● 預設1MB,可透過連結器的/HEAP參數指定● 最大的一塊大約是1.5GB

Page 25: 程式設計師的自我修養 Chapter 10 記憶體

堆積分配演算法

● 堆積只是程式向作業系統申請劃出來的一塊位址空間

● 如何管理一大塊連續的記憶體空間並按照需求分配﹑釋放空間。○ 空間串列○ 點陣圖○ 物件集區

Page 26: 程式設計師的自我修養 Chapter 10 記憶體

空間串列

把堆積中各個空閒的區塊按照串列的方式連接起來,當使用者請求一塊空間時,可以遊走整個列表,直到找到合適大小的區塊並且將它拆分;當使用者釋放空間時將它合併到空閒串列中。

Page 27: 程式設計師的自我修養 Chapter 10 記憶體

點陣圖

將整個堆積劃分為大量大小相同的區塊

﹙block﹚,當請求記憶體時,總是分配整數個的

空間。

Page 28: 程式設計師的自我修養 Chapter 10 記憶體

物件集區

如果每一次分配的空間大小都一樣,依把整個堆積空間劃分為大量的小塊,每次請求的時候只需要找到一個小塊就可以了。

Page 29: 程式設計師的自我修養 Chapter 10 記憶體

現實應用

● 堆積分配演算法往往採取多演算法複合而成● 以glibc來講,

○ < 64-byte︰物件集區○ < 512-byte︰物件集區和最佳適配演算法的最佳折衷○ < 128-KB︰最佳適配演算法○ > 128-KB︰mmap()

● More: glibc中malloc的详细解释

Page 30: 程式設計師的自我修養 Chapter 10 記憶體

參考實作

● jemallochttp://www.canonware.com/jemalloc/

● nedmallochttp://www.nedprod.com/programs/portable/nedmalloc/