之前一直在分析棧,棧這個東西的作用也介紹得差不多了, 但是棧在哪兒還沒有搞清楚,以及堆、代碼、全局變量它們在哪兒, 這都牽涉到進程的內存分佈。
內存分佈隨著操作系統的更新換代,越來越科學合理, 也越來越複雜,所以我們還是先了解一下早期操作系統的典型 linux 0.01 的進程的內存分佈:
linux 0.01 的一個進程固定擁有64MB的線性內存空間 (ACM競賽中單個程序的最大內存佔用限制為64MB, 這肯定有貓膩O(∩_∩)O~),各個進程挨個放置在一張頁目錄表中, 一個頁目錄表可管理4G的線性空間,因此 linux0.01 最多有 64個進程。每個進程的內存分佈如下:
.text .rodata .data .bss
是常駐內存的, 也就是說進程從開始運行到進程僵死它們一直蹲在那裡, 所以訪問它們用的是常量地址;而棧是不斷的加幀(函數調用) 、減幀(函數返回)的,幀內的局部變量只能用相對於當前 esp(指向棧頂)或 ebp(指向當前幀)的相對地址來訪問。
棧被放置在高地址也是有原因的: 調用函數(加幀)是減 esp 的,函數返回(減幀)是加 esp 的, 調用在前,所以棧是向低地址擴展的,放在高地址再合適不過了。
認識了 linux 0.01 的內存分佈後, 再看看現代操作系統的內存分佈發生了什麼變化:
首先,linux 0.01 進程的64MB內存限制太過時了, 現在的程序都有潛力使用到 2GB、3GB 的內存空間 (每個進程一張頁目錄表),當然,機器有硬傷的話也沒辦法, 我的電腦就只有 2GB 的內存,想用 3GB 的內存是沒指望了。 但也不是有4GB內存就可以用4GB(32位), 因為操作系統還要佔個坑呢! 現代 linux 中 0xC0000000 以上的 1GB 空間是操作系統專用的, 而 linux 0.01 中第1個 64MB 是操作系統的坑, 所以別的進程完全佔有它們的 64MB, 也不用跟操作系統客氣。
其次,linux 0.01只有進程沒有線程, 但是現代 linux 有多線程了 (linux 的線程其實是個輕量級的進程), 一個進程的多個線程之間共享全局變量、堆、打開的文件…… 但棧是不能共享的:棧中各層函數幀代表著一條執行線索, 一個線程是一條執行線索,所以每個線程獨佔一個棧, 而這些棧又都必須在所屬進程的內存空間中。
根據以上兩點,進程的內存分佈就變成了下面這個樣子:
再者,如果把動態裝載的動態鏈接庫也考慮進去的話, 上面的分佈圖將會更加"破碎"。
如果我們的程序沒有采用多線程的話, 一般可以簡單地認為它的內存分佈模型是 linux 0.01 的那種。