九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
程序運(yùn)行時的內(nèi)存空間分布
我們在寫程序時,既有程序的邏輯代碼,也有在程序中定義的變量等數(shù)據(jù),那么當(dāng)我們的程序進(jìn)行時,我們的代碼和數(shù)據(jù)究竟是存放在哪里的呢?下面就來總結(jié)一下。

一、程序運(yùn)行時的內(nèi)存空間情況

其實(shí)在程序運(yùn)行時,由于內(nèi)存的管理方式是以頁為單位的,而且程序使用的地址都是虛擬地址,當(dāng)程序要使用內(nèi)存時,操作系統(tǒng)再把虛擬地址映射到真實(shí)的物理內(nèi)存的地址上。所以在程序中,以虛擬地址來看,數(shù)據(jù)或代碼是一塊塊地存在于內(nèi)存中的,通常我們稱其為一個段。而且代碼和數(shù)據(jù)是分開存放的,即不儲存于同于一個段中,而且各種數(shù)據(jù)也是分開存放在不同的段中的。

下面以一個簡單的程序來看一下在Linux下的程序運(yùn)行空間情況,代碼文件名為space.c

1
2
3
4
5
6
7
8
9
#include <unistd.h>  
#include <stdio.h>  
  
int main()  
{  
    printf("%d\n", getpid());  
    while(1);  
    return 0;  
}

這個程序非常簡單,輸出當(dāng)前進(jìn)程的進(jìn)程號,然后進(jìn)入一個死循環(huán),這個死循環(huán)的目的只是讓程序不退出。而在Linux下有一個目錄/proc/$(pid),這個目錄保存了進(jìn)程號為pid的進(jìn)程運(yùn)行時的所有信息,其中有一個文件maps,它記錄了程序執(zhí)行過程中的內(nèi)存空間的情況。編譯運(yùn)行上面的代碼,其運(yùn)行結(jié)果如圖1所示:


從上面的圖中,我們可以看到這樣一個簡單的程序,在執(zhí)行時,需要哪些庫和哪些空間。上面的圖的各列的意思,不一一詳述,只對重要的進(jìn)行說明。

第一列的是一個段的起始地址和結(jié)束地址,第二列這個段的權(quán)限,第三列段的段內(nèi)相對偏移量,第六列是這個段所存放的內(nèi)容所對應(yīng)的文件。從上圖可以看到我們的程序進(jìn)行首先要加載系統(tǒng)的兩個共享庫,然后再加載我們寫的程序的代碼。

對于第二列的權(quán)限,r:表示可讀,w:表示可寫,x:表示可執(zhí)行,p:表示受保護(hù)(即只對本進(jìn)程有效,不共享),與之相對的是s,意是就是共享。

從上圖我們可以非常形象地看到一個程序進(jìn)行時的內(nèi)存分布情況。下面我們將會結(jié)合上圖,進(jìn)行更加深入的對內(nèi)存中的數(shù)據(jù)段的解說。

二、程序運(yùn)行時內(nèi)存的各種數(shù)據(jù)段

1.bss 段

該段用來存放沒有被初始化或初始化為0的全局變量,因?yàn)槭侨肿兞浚栽诔绦蜻\(yùn)行的整個生命周期內(nèi)都存在于內(nèi)存中。有趣的是這個段中的變量只占用程序運(yùn)行時的內(nèi)存空間,而不占用程序文件的儲存空間??梢杂靡韵鲁绦騺碚f明這點(diǎn),文件名為bss.c

1
2
3
4
5
6
7
8
#include <stdio.h>  
  
int bss_data[1024 * 1024];  
  
int main()  
{  
    return 0;  
}

這個程序非常簡單,定義一個4M的全局變量,然后返回。編譯成可執(zhí)行文件bss,并查看可執(zhí)行文件的文件屬性如圖2所示:

從可執(zhí)行文件的大小4774B可以看出,bss數(shù)據(jù)段(4M)并不占用程序文件的儲存空間,在下面的data段中,我們可以看到data段的數(shù)據(jù)是占用可執(zhí)行文件的儲存空間的。

在圖1中,有文件名且屬性為rw-p的內(nèi)存區(qū)間,就是bss段。

2.data段

初始化過的全局變量數(shù)據(jù)段,該段用來保存初始化了的非0的全局變量,如果全局變量初始化為0,則編譯有時會出于優(yōu)化的考慮,將其放在bss段中。因?yàn)橐彩侨肿兞浚栽诔绦蜻\(yùn)行的整個生命周期內(nèi)都存在于內(nèi)存中。與bss段不同的是,data段中的變量既占程序運(yùn)行時的內(nèi)存空間,也占程序文件的儲存空間??梢杂孟旅娴某绦騺碚f明,文件名為data.c:

1
2
3
4
5
6
7
8
#include <stdio.h>  
  
int data_data[1024 * 1024] = {1};  
  
int main()  
{  
    return 0;  
}

這個程序與上面的bss唯一的不同就是全局變量int型數(shù)組data_data,其中第0個元素的值初始化為1,其他元素的值初始化成默認(rèn)的0,而因?yàn)閿?shù)組的地址是連續(xù)的,所以只要有一個元素在data段中,則其他的元素也必然在data段中。編譯連接成可執(zhí)行文件data,并查看可執(zhí)行文件的文件屬性如圖3所示:

從可執(zhí)行文件的大小來看,data段數(shù)據(jù)(data_data數(shù)組的大小,4M)占用程序文件的儲存空間。

在圖1中,有文件名且屬性為rw-p的內(nèi)存區(qū)間,就是data段,它與bss段在內(nèi)存中是共用一段內(nèi)存的,不同的是,bss段數(shù)據(jù)不占用文件,而data段數(shù)據(jù)占用文件儲存空間。

3.rodata段

該段是常量數(shù)據(jù)段,用于存放常量數(shù)據(jù),ro就是Read Only之意。但是注意并不是所有的常量都是放在常量數(shù)據(jù)段的,其特殊情況如下:

1)有些立即數(shù)與指令編譯在一起直接放在代碼段(text段,下面會講到)中。
2)對于字符串常量,編譯器會去掉重復(fù)的常量,讓程序的每個字符串常量只有一份。
3)有些系統(tǒng)中rodata段是多個進(jìn)程共享的,目的是為了提高空間的利用率。

在圖1中,有文件名的屬性為r–p的內(nèi)存區(qū)間就是rodata段。可見他是受保護(hù)的,只能被讀取,從而提高程序的穩(wěn)定性。

4.text段

text段就是代碼段,用來存放程序的代碼(如函數(shù))和部分整數(shù)常量。它與rodata段的主要不同是,text段是可以執(zhí)行的,而且不被不同的進(jìn)程共享。

在圖1中,有文件名且屬性為r-xp的內(nèi)存區(qū)間就是text段。就如我們所知道的那樣,代碼段是不能被寫的。

5.stack段

該段就是棧段,用來保存臨時變量和函數(shù)參數(shù)。程序中的函數(shù)調(diào)用就是以棧的方式來實(shí)現(xiàn)的,通常棧是向下(即向低地址)增長的,當(dāng)向棧中push一個元素,棧頂指針就會向低地址移動,當(dāng)從棧中pop一個元素,棧頂指針就會向高地址移動。棧中的數(shù)據(jù)只在當(dāng)前函數(shù)或下一層函數(shù)中有效,當(dāng)函數(shù)返回時,這些數(shù)據(jù)自動被釋放,如果繼續(xù)對這些數(shù)據(jù)進(jìn)行訪問,將發(fā)生未知的錯誤。通常我們在程序中定義的不是用malloc系統(tǒng)函數(shù)或new出來的變量,都是存放在棧中的。例如,如下函數(shù):

1
2
3
4
5
6
void func()  
{  
    int a = 0;  
    int *n_ptr = malloc(sizeof(int));  
    char *c_ptr = new char;  
}

整型變量a,整型指針變量n_ptr和char型指針變量c_ptr,都存放在棧段中,而n_ptr和c_ptr指向的變量,由于是malloc或new出來的,所以存放在堆中。當(dāng)函數(shù)func返回時,a、n_ptr、c_ptr都會被釋放,但是n_ptr和c_ptr指向的內(nèi)存卻不會釋放。因?yàn)樗鼈兪谴嬖谟诙阎械臄?shù)據(jù)。

在圖1中,文件名為stack的內(nèi)存區(qū)間即為棧段。

6.heap 段

heap(堆)是最自由的一種內(nèi)存,它完全由程序來負(fù)責(zé)內(nèi)存的管理,包括什么時候申請,什么時候釋放,而且對它的使用也沒有什么大小的限制。在C/C++中,用alloc系統(tǒng)函數(shù)和new申請的內(nèi)存都存在于heap段中。

以上面的程序?yàn)槔?,它向堆申請了一個int和一個char的內(nèi)存,因?yàn)闆]有調(diào)用free或delete,所以當(dāng)函數(shù)返回時,堆中的int和char變量并沒有釋放,造成了內(nèi)存泄漏。

由于在圖1所對應(yīng)的代碼中沒有使用alloc系統(tǒng)函數(shù)或new來申請內(nèi)存,所以heap段并沒有在圖1中顯示出來,所以以下面的程序來說明heap段的位置,代碼文件為heap.c,代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
  
int main()  
{  
    int *n_ptr = malloc(sizeof(int));  
    printf("%d\n", getpid());  
    while(1);  
    free(n_ptr);  
    return 0;  
}

查看其運(yùn)行時內(nèi)存空間分布如下:

可以看到文件名為heap的內(nèi)存區(qū)間就是heap段。從上圖,也可以看出,雖然我們只申請4個字節(jié)(sizeof(int))的空間,但是在操作系統(tǒng)中,內(nèi)存是以頁的方式進(jìn)行管理的,所以在分配heap內(nèi)存時,還是一次分配就為我們分配了一個頁的內(nèi)存。注:無論是圖1,還是上圖,都有一些沒有文件名的內(nèi)存區(qū)間,其實(shí)沒用文件名的內(nèi)存區(qū)間表示使用mmap映射的匿名空間。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
程序分text, data (initialized), bss, stack, heap幾個段 - C Language Syntax - 木瓜老C
匯編中bss,data,text,rodata,heap,stack的意義
BSS段、數(shù)據(jù)段、代碼段、堆與棧
深入探析C語言內(nèi)存分配原理(上):理解內(nèi)存使用的魔力密碼
字符串shellcode在house of force中的運(yùn)用
程序中的數(shù)據(jù)在內(nèi)存中的布局
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服