Linux下x86_64进程地址空间布局
在x86_64下和i386下是類似的,本文主要關注vm.legacy_va_layout以及kernel.randomize_va_space參數影響下的進程空間內存宏觀布局,以及vDSO和多線程下的堆和棧分布。
情形一:
- vm_legacy_va_layout=1
- kernel.randomize_va_space=0
此種情況下采用傳統內存布局方式,不開啟隨機化,程序的內存布局
可以看出: 代碼段:0x400000–> 數據段 堆:向上增長 2aaaaaaab000–> 棧:7ffffffde000<–7ffffffff000 系統調用:ffffffffff600000-ffffffffff601000 你可以試一下其他程序,在kernel.randomize_va_space=0時堆起點是不變的
情形二:
- vm_legacy_va_layout=0
- kernel.randomize_va_space=0
現在默認內存布局,不隨機化
可以看出: 代碼段:0x400000–> 數據段 堆:向下增長 <–7ffff7fff000 棧:7ffffffde000<–7ffffffff000 系統調用:ffffffffff600000-ffffffffff601000
情形三:
- vm_legacy_va_layout=0
- kernel.randomize_va_space=2 //ubuntu 14.04默認值 使用現在默認布局,隨機化
對比兩次啟動的cat程序,其內存布局堆的起點是變化的,這從一定程度上防止了緩沖區溢出攻擊。
情形四:
- vm_legacy_va_layout=1
- kernel.randomize_va_space=2 //ubuntu 14.04默認值 與情形三類似,不再贅述
vDSO
在前面談了兩個不同參數下的進程運行時內存空間宏觀的分布。也許你會注意到這樣一個細節,在每個進程的stack以上的地址中,有一段動態變化的映射地址段,比如下面這個進程,映射到vdso。 >?
如果我們用ldd看相應的程序,會發現vdso在磁盤上沒有對應的so文件。 不記得曾經在哪里看到大概這樣一個問題: > getpid,gettimeofday是不是系統調用?
其實這個問題的答案就和vDSO有關,雜x86_64和i386上,getpid是系統調用,而gettimeofday不是。
vDSO全稱是virtual dynamic shared object,是一種內核將一些本身應該是系統調用的直接映射到用戶空間,這樣對于一些使用比較頻繁的系統調用,直接在用戶空間調用可以節省開銷。如果想詳細了解,可以參考這篇文檔
下面我們用一段程序驗證下:
int main(int argc, char **argv) {struct timeval tv;int ret;if ((ret=gettimeofday(&tv, NULL))<0) {fprintf(stderr, "gettimeofday call failed\n");}else{fprintf(stdout, "seconds:%ld\n", (long int)tv.tv_sec);}fprintf(stdout, "pid:%d\n", (int)getpid());fprintf(stdout, "thread id:%d\n", (int)syscall(SYS_gettid));return 0; }編譯為可執行文件后,我們可以用strace來驗證:
strace -o temp ./vdso grep getpid temp grep gettimeofday temp多線程的堆棧
- 三個線程的進程:
- 主線程:
- 子線程1:
- 子線程2:
- 測試代碼1:
- 測試代碼2:打印內核棧地址
- 對應init進程的內核棧stack起始地址
- 用戶態線程棧在同一進程空間的堆起始部分分配,x86_64默認是8M,可以通過ulimit等方法設置
- 用戶態線程棧的增長是從低的線性地址往高增長
- 內核棧位于高地址
- 主線程的棧(姑且稱為進程棧吧)行為比較怪異,后面會詳細分析glibc的ptmalloc下多線程程序malloc和線程棧的內存分配行為
總結
以上是生活随笔為你收集整理的Linux下x86_64进程地址空间布局的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 第7章 数组
- 下一篇: Django里自定义用户登陆及登陆后跳转