關於嵌入式系統內存地址空間的一些疑問(.text、.data、.bass、堆\棧空間)

深入淺出瞭解(.text、.data、.bss、堆空間、棧空間)的含義

接下來所說的是嵌入式系統的內存地址空間的佈局,簡單的說就是我們寫好的代碼,在編譯過程種中,把代碼裏不同的變量、函數相應的保存在每個段中(.text、.data、.bss),至於堆空間、棧空間是代碼在芯片上運行時才存在的。

  1. .text:代碼段。包含了操作系統和應用程序的所有代碼。
  2. .data:數據段。存放了操作系統和應用程序當中所有帶有初始值的全局變量。
  3. .bss:bss段。存放了操作系統和應用程序當中所有未帶初始化的全局變量。
  4. 堆空間:動態分配的內存空間。在系統運行時,可以通過malloc/free之類的函數來申請或者釋放一段連續的內存空間
  5. 棧空間:保存運行上下文以及函數調用時的局部變量和形參。

那麼問題1,什麼樣的數據會保存在.text段裏?.data段?.bss段?棧空間?堆空間?

答:下面舉例說明:

unsigned char gvCh;//全局變量,沒有初值,放在.bss段中
unsigned short gvShort;// 全局變量,沒有初值,放在.bss段中
unsigned int gvInt = 0x12345678;//全局變量,有初值,放在.data段中
unsigned long gvLong = 0x87654321; //全局變量,有初值,放在.data段中

int main(void)//main函數在經過編譯以後得到的機器代碼,存放在.text代碼段當中
{
    unsigned char arry[10],*p;//局部變量,存放在棧當中
    p = malloc(10*sizeof(char));//p指針變量指向的空間,存放在堆當中
    while(1);
}

補充:上面定義的全局變量沒有在代碼上引用,這裏只是舉例,如果在實際應用中,它們會被編譯器優化掉,那麼它們根本就不會佔用內存空間。

問題2,上面的例子可能只是給你解開了一部分疑惑,如果用上變量修飾詞static、const,那麼它們又是怎麼存儲的呢?

答:下面舉例說明:

static unsigned char gvCh;//全局變量,只能在本文件內引用,沒有初值,放在.bss段中
const unsigned short gvShort;//全局變量,不可修改(只讀),放在.data段中。實際不會定義無初值的const變量
static unsigned int gvInt = 0x12345678;//全局變量,只能在本文件內引用,有初值,放在.data段中
const unsigned long gvLong = 0x87654321;//全局變量,不可修改(只讀),放在.data段中。

int main(void)
{
    static unsigned char lvCh;//加上static的局部變量,只能在本函數內引用,函數結束回時不會消失,沒有初值,放在.bss段中
    const unsigned short lvShort;//加上const的局部變量,不可修改(只讀),放在.data段中。實際不會定義無初值的const變量
    static unsigned int lvInt = 0x12345678;//加上static的局部變量,只能在本函數內引用,函數結束回時不會消失,有初值,放在.data段中
    const unsigned long lvLong = 0x87654321//加上const局部變量,不可修改(只讀),放在.data段中
    while(1);
}

總結,如果變量加上const,不管是全局變量還是局部變量,不管有沒有初值,都保存在.data段。如果變量加上static,不會影響變量所保存的段,static的作用是改變變量的作用域。

問題3,我們瞭解完怎麼樣的變量該保存到什麼段,或許還會有疑惑,.data段的數據和.bss段的數據有什麼區別?把數據這麼區分出來的作用是什麼?

答:原因其實很簡單,就是爲了節省編譯出來的bin文件佔用的內存大小。.data段變量的值會記錄在bin二進制文件中,而.bss記錄的是變量的起始地址和大小,在程序運行時初始化爲零。下舉例說明:

unsigned char gvCh_init[3] = {1,2,3}; //.data段,如果數組增大100字節,那麼bin文件大小會也會隨之增大
unsigned char gvCh_no_init[3];//.bss段,如果數組增大爲100字節,那麼bin文件大小不會發生變化,在bin文件裏只是記錄這個數組的起始地址和大小,當程序運行時自動把它的值清零。

問題4,unsigned chat gvCh[100] ={0};算不算給數組gvCh設置了初值而被保存在.data段,將佔用bin文件大小?

答:在IAR編譯器裏,unsigned chat gvCh[100] ={0}等同於unsigned chat gvCh[100],保存於.bss段。

問題5,我們知道在芯片上有ROM存儲器和RAM存儲器,在程序bin文件燒錄到芯片上時,不同的段會如何存在於在ROM和RAM裏呢?

答:沒有一一對應的關係。因爲我可以把bin文件燒錄到flash(ROM)裏面運行,也可以把它燒錄到RAM裏面運行(調試時會這麼做)。

通常地,我們會把編譯好的程序燒錄到flash(ROM)裏面去,芯片掉電時不會消失;對於RAM存儲器來說,只有芯片上電代碼運行起來,纔會被分配使用(棧/堆就是在RAM開闢的)。

問題6,代碼中的變量會以什麼樣的規則存在於ROM存儲器和RAM存儲器裏?

答:主要根據變量在運行時是否可修改,下面舉例說明:

unsigned char gvCh;////可讀寫,存放在RAM(無初始值,.bss段)
unsigned int gvInt = 0x12345678;//可讀寫,存放在RAM(有初始值,.data段)
const unsigned long gvLong = 0x87654321; //只讀,存放在ROM

int main1(void)//main函數在經過編譯以後得到的機器代碼,不可修改,存放ROM
{
    unsigned char arry[10],*p;//可讀寫,存放在棧當中,也就是在RAM中
   
    p = malloc(10*sizeof(char));//p指針變量指向的空間,存放在堆當中,也就是在RAM中
    while(1);
}

有初始值、並且可讀寫的全局變量gvInt存放在RAM,但我們知道在RAM的數據掉電會丟失,其實它的初始值0x12345678一開始保存在flash(ROM)裏,在芯片上電時,會將初始值複製到RAM裏的gvInt變量(它所在RAM的地址在編譯後已經確定)。可能我們會疑惑,自己的代碼明明沒有寫給gvInt變量賦值的過程,這一部分代碼並不需要我們做。芯片上電就會進入Reset_Handler中斷,打開.s啓動文件,我們可以發現在Reset_Handler中斷裏,調用了SystemInit函數,然後再調用了_main(IAR編譯器的話是__iar_program_start)函數,在_main函數會進行RW data的複製,和.bss段的初始化,以及C庫函數的初始化(比如malloc函數需要初始化才能使用),進而調用main函數進入我們的代碼區域。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章