linux進程地址空間佈局淺析

此文章是圍繞該文章 的思路進行總結的,原文記錄的筆記詳盡,在此基礎上,我總結了本身的一下(想對淺顯)。
若是對具體細節想要了解的,能夠訪問這篇文章,在文章最後付了一段代碼,跟該成程序的內存映射圖,若是有哪裏寫的不正確,歡迎補充和指正。linux

程序是什麼

抽象的來講當咱們打開電腦點擊應用程序圖標,一個程序就運行起來了,
可是在操做系統層面來看,實際上是產生了一個進程 ,這是一個程序的實體。ios

接下來以linux操做系統來介紹linux中進程的各個段和其表明的含義c++

linux虛擬地址空間

在linux操做系統中,每當生成一個進程時,它就具備了一個虛擬地址空間,一般在32位系統中,它的大小爲4GB,按3:1的比例分配給用戶空間和內核空間。程序員

虛擬地址空間的簡單工做原理就是將虛擬地址經過頁表映射到實際物理地址(因此在c語言中malloc分配內存時,即便是malloc一個int大小的空間,可是其實是分配了一個頁的內存,通常是4096個字節)編程

用戶內存空間的各個段分佈

代碼段:可執行的代碼,字符串常量等

數據段: 1.數據段:已初始化的,且初值爲非0的全局變量(static變量)
        2.bss段:未初始化,或者初值爲0的全局變量 (static變量)
        
堆:能夠理解爲程序員在程序中本身爲變量分配的內存(需手動釋放)

棧:保存了函數被調用時所須要的局部變量,函數參數,返回地址。
    該進程在派生出不少線程時,線程棧也保存在這裏。

棧說到底也是一種數據結構,它遵循着先進後出的順序,因此特別適合函數的執行(進棧),函數返回(出棧)。
堆棧的用途分三種:
1.爲函數內部聲明的局部變量(非靜態局部變量)分配內存空間。
2.記錄函數調用信息,用棧幀表示。除此以外還有函數的返回地址,一些進出寄存器的變量。
3.臨時存儲區,用於暫存長算術表達式部分計算結果或alloca()函數分配的棧內內存。(不是很瞭解)數據結構

在此基礎上,因此會常常引起一個編程上的問題,即:棧溢出, 這種狀況在遞歸調用中出現的尤爲多,
除此以外還有申請了內存,沒有釋放,也極可能形成內存泄露。避免這種狀況,就須要本身注意內存的
釋放,通常一個new對應一個的delete。多線程

在linux中能夠用ulimit -s 來查看堆棧的大小,下面的是我linux系統中的堆棧大小架構

[m@m ~]$ ulimit -s
8192

堆是程序運行時動態分配內存的內存段,它一般用malloc和new(底層也是malloc)來得到一段內存空間,用
free和delete來釋放已經使用完畢的內存空間。app

常見問題:

因爲是經過程序員本身手動建立,因此必定要注意內存釋放,問題,在類,main函數中動態分配的內存,
通常都能釋放,可是有時在其餘函數中new出來的內存空間,、並無delete掉,致使內存泄露。
屢次運行函數,可能程序會崩潰。函數

#include <iostream>

int* exp(int i){
        int *ptr = new int(i);
        return ptr;
}

int main(){

        int a = 1;
        int *p = exp(a);

        // delete ptr; // 錯誤! 因爲函數中的ptr已經被回收,因此不能delete ptr
        delete p; // 釋放定義在函數外部的指針是正確的
        p = NULL;
        return 0;
}

下面這句話是引用:

使用堆時常常出現兩種問題:1) 釋放或改寫仍在使用的內存(「內存破壞」);2)未釋放再也不使用的內存(「內存泄漏」)。當釋放次數少於申請次數時,可能已形成內存泄漏。泄漏的內存每每比忘記釋放的數據結構更大,由於所分配的內存一般會圓整爲下個大於申請數量的2的冪次(如申請212B,會圓整爲256B)。

代碼段

代碼段也稱正文段或文本段,一般用於存放程序執行代碼(即CPU執行的機器指令)。通常C語言執行語句都編譯成機器代碼保存在代碼段。一般代碼段是可共享的,所以頻繁執行的程序只須要在內存中擁有一份拷貝便可。代碼段一般屬於只讀,以防止其餘程序意外地修改其指令(對該段的寫操做將致使段錯誤)。某些架構也容許代碼段爲可寫,即容許修改程序。

數據段

數據段一般用於存放程序中已初始化且初值不爲0的全局變量(也包括靜態全局變量)和靜態局部變量

bss段

bss段一般存放如下兩種狀況:
1.未初始化的全局變量(靜態全局變量)和局部靜態變量
2.初始化爲0的全局變量(靜態全局變量)和靜態局部變量

內存映射區

除了這些以外,夾在stack和heap之間還有一個內存映射段(mmap
如下三段爲引用

此處,內核將硬盤文件的內容直接映射到內存, 任何應用程序均可經過Linux的mmap()系統調用或Windows的CreateFileMapping()/MapViewOfFile()請求這種映射。內存映射是一種方便高效的文件I/O方式, 於是被用於裝載動態共享庫。用戶也可建立匿名內存映射,該映射沒有對應的文件, 可用於存放程序數據。在 Linux中,若經過malloc()請求一大塊內存,C運行庫將建立一個匿名內存映射,而不使用堆內存。」大塊」 意味着比閾值 MMAP_THRESHOLD還大,缺省爲128KB,可經過mallopt()調整。

最後附上一段本身寫的代碼用來查看內存佈局

Linux下在/proc/進程號/maps 查看內存映射

源代碼:
    #include <iostream>

    void get(){
            int e;
            int f = 4;
            static int g;
            static int h = 5;
            std::cout << "in get(), the address a is " << &e << std::endl;
            std::cout << "in get(), the address b is " << &f << std::endl;
            std::cout << "in get(), the address c is " << &g << std::endl;
            std::cout << "in get(), the address d is " << &h << std::endl;
            while(1);  // 讓程序死在這用來查看內存映射表
    }
int main(){

            int a = 1;
            int b;
            static int c = 2;
            static int d;
            int *p = new int(3);
            std::cout << "in main, the address a is " << &a << std::endl;
            std::cout << "in main, the address b is " << &b << std::endl;
            std::cout << "in main, the address c is " << &c << std::endl;
            std::cout << "in main, the address d is " << &d << std::endl;
            std::cout << "in main, the address p is " << p << std::endl;
            get();  // 程序在這進入死循環

            delete p;
            p = NULL;
            return 0;
    }

運行結果:
    in main, the address a is 0x7ffd270384a8
    in main, the address b is 0x7ffd270384a4
    in main, the address c is 0x602074
    in main, the address d is 0x60219c
    in main, the address p is 0x1c06c20
    in get(), the address a is 0x7ffd2703844c
    in get(), the address b is 0x7ffd27038448
    in get(), the address c is 0x602198
    in get(), the address d is 0x602070

    
在內存中,各個變量所處在各個段的虛擬地址:
    [m@m ~/practice]$ cat /proc/16138/maps 
    00400000-00401000 r-xp 00000000 08:06 2364212                            /home/m/practice/12.out
    00601000-00602000 r--p 00001000 08:06 2364212                            /home/m/practice/12.out
    00602000-00603000 rw-p 00002000 08:06 2364212                            /home/m/practice/12.out
    01bf5000-01c27000 rw-p 00000000 00:00 0                                  [heap]
    7fa3c231b000-7fa3c24b0000 r-xp 00000000 08:07 2361747                    /lib/x86_64-linux-gnu/libc-2.24.so
    7fa3c24b0000-7fa3c26b0000 ---p 00195000 08:07 2361747                    /lib/x86_64-linux-gnu/libc-2.24.so
    7fa3c26b0000-7fa3c26b4000 r--p 00195000 08:07 2361747                    /lib/x86_64-linux-gnu/libc-2.24.so
    7fa3c26b4000-7fa3c26b6000 rw-p 00199000 08:07 2361747                    /lib/x86_64-linux-gnu/libc-2.24.so
    7fa3c26b6000-7fa3c26ba000 rw-p 00000000 00:00 0 
    7fa3c26ba000-7fa3c26d0000 r-xp 00000000 08:07 2359364                    /lib/x86_64-linux-gnu/libgcc_s.so.1
    7fa3c26d0000-7fa3c28cf000 ---p 00016000 08:07 2359364                    /lib/x86_64-linux-gnu/libgcc_s.so.1
    7fa3c28cf000-7fa3c28d0000 r--p 00015000 08:07 2359364                    /lib/x86_64-linux-gnu/libgcc_s.so.1
    7fa3c28d0000-7fa3c28d1000 rw-p 00016000 08:07 2359364                    /lib/x86_64-linux-gnu/libgcc_s.so.1
    7fa3c28d1000-7fa3c29d4000 r-xp 00000000 08:07 2361751                    /lib/x86_64-linux-gnu/libm-2.24.so
    7fa3c29d4000-7fa3c2bd3000 ---p 00103000 08:07 2361751                    /lib/x86_64-linux-gnu/libm-2.24.so
    7fa3c2bd3000-7fa3c2bd4000 r--p 00102000 08:07 2361751                    /lib/x86_64-linux-gnu/libm-2.24.so
    7fa3c2bd4000-7fa3c2bd5000 rw-p 00103000 08:07 2361751                    /lib/x86_64-linux-gnu/libm-2.24.so
    7fa3c2bd5000-7fa3c2d47000 r-xp 00000000 08:07 3676435                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
    7fa3c2d47000-7fa3c2f47000 ---p 00172000 08:07 3676435                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
    7fa3c2f47000-7fa3c2f51000 r--p 00172000 08:07 3676435                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
    7fa3c2f51000-7fa3c2f53000 rw-p 0017c000 08:07 3676435                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
    7fa3c2f53000-7fa3c2f56000 rw-p 00000000 00:00 0 
    7fa3c2f56000-7fa3c2f79000 r-xp 00000000 08:07 2359411                    /lib/x86_64-linux-gnu/ld-2.24.so
    7fa3c3151000-7fa3c3155000 rw-p 00000000 00:00 0 
    7fa3c3176000-7fa3c3179000 rw-p 00000000 00:00 0 
    7fa3c3179000-7fa3c317a000 r--p 00023000 08:07 2359411                    /lib/x86_64-linux-gnu/ld-2.24.so
    7fa3c317a000-7fa3c317b000 rw-p 00024000 08:07 2359411                    /lib/x86_64-linux-gnu/ld-2.24.so
    7fa3c317b000-7fa3c317c000 rw-p 00000000 00:00 0 
    7ffd27018000-7ffd2703b000 rw-p 00000000 00:00 0                          [stack]
    7ffd2704a000-7ffd2704c000 r--p 00000000 00:00 0                          [vvar]
    7ffd2704c000-7ffd2704e000 r-xp 00000000 00:00 0                          [vdso]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
相關文章
相關標籤/搜索