筆記:程序內存管理 .bss .data .rodata .text stack heap


1.未初始化的全局變量(.bss段)

bss段用來存放 沒有被初始化已經被初始化為0 的全局變量。如下例代碼:

#include<stdio.h>

int bss_array[1024*1024];
int main(int argc, char *argv[])
{  
    return 0;
}

 編譯並查看:

$ gcc -g mainbss.c -o mainbss
$ ls -l mainbss
-rwxrwxr-x. 1 hy hy 8330 Apr 22 19:33 mainbss
$ objdump -h mainbss |grep bss
mainbss:     file format elf32-i386
 24 .bss          00400020  0804a020  0804a020  00001018  2**5
$ size mainbss
   text	   data	    bss	    dec	    hex	filename
   1055	    272	4194336	4195663	 40054f	mainbss

     全局變量bss_array的大小為4MB = 1024*1024*sizeof(int) Byte = 4194304 Byte。 通過size 查看可知數據被存在了 bss 段

  而 可執行文件mainbss 有8KB左右,命令 ls -l mainbss 查看得知。可知,bss類型的全局變量只占用 運行時的內存空間,而不占用可執行文件自身的文件空間

 

2017-11-24

    今天再讀此書時,聯想到 OR1200 編譯過程中的 RAM 和 鏈接腳本,產生一個問題。在 OR 仿真驗證中,我的RAM只有8KB的空間,若我有一個定義上訴數組,可執行文件本身不大,但是運行時需要占用4M的空間。那程序豈不是會崩潰? 帶着這個問題。在OR的驗證程序中做了測試。

個人結論: 若在運行時這個空間不足的問題,編譯器會幫忙檢查的。這些是數據,若是在運行時的 堆和棧 不足,這點編譯器沒法檢查。

#include"uart.h"
volatile unsigned long timestamp = 0;
#define DRAM_MEM_SIZE (0x200) #define DRAM_ADDR_START (0x1FFF-DRAM_MEM_SIZE+1) #define DRAM_ADDR_END (0x1FFF) #define Struct_Section __attribute__((unused, section(".stdata_mem_type"), \ aligned(4))) typedef unsigned int u32; typedef signed int s32; char txbuf[]={0x01,0x03,0x07,0x0f,0x10,0x30,0x70}; char testbss; char testdata=10; typedef struct { char ch[4]; unsigned int addrinit; unsigned int len; }STDATA_MEM_TYPE; STDATA_MEM_TYPE arry_dynamic Struct_Section; int t_array[1024*1024]={}; int main(void) { //省略 }

編譯報錯如下, .bss 段不夠用:

openrisc@openrisc-VirtualBox:~/mc-or-lngit/or_bootsim_shpy/hyor_ramboot$ make
or32-elf-gcc -c reset.S -o reset.o
or32-elf-gcc -c main.c -o main.o
or32-elf-gcc -c uart.c -o uart.o 
or32-elf-ld reset.o main.o uart.o -T ram.ld -o hyor_ramboot.or32 
or32-elf-ld: hyor_ramboot.or32 section `.bss' will not fit in region `ram'
or32-elf-ld: region `ram' overflowed by 4192276 bytes
make: *** [hyor_ramboot.or32] Error 1

 

2. 已被初始化為非零的全局變量(.data段)

  data段用來存放已經被初始化為非零的全局變量。如下代碼,只將矩陣的第一個元素初試化為1:

#include<stdio.h>

int data_array[1024*1024]={1};
int main(int argc, char *argv[])
{ 
    return 0;
}

 編譯查看

[hy@localhost memcfg]$ gcc -g maindata.c -o maindata
[hy@localhost memcfg]$ ls -l maindata
-rwxrwxr-x. 1 hy hy 4202682 Apr 22 19:48 maindata
[hy@localhost memcfg]$ objdump -h maindata |grep \\.data
 23 .data         00400020  0804a020  0804a020  00001020  2**5
[hy@localhost memcfg]$ size maindata
   text	   data	    bss	    dec	    hex	filename
   1055	4194604	      4	4195663	 40054f	maindata 

 而 可執行文件maindata 有4MB左右。通過size 查看可知數據被存在了 data 段

可知,data類型的全局變量既占用運行時的內存空間,也占用可執行文件自身的文件空間

3.常量數據(.rodata段)

 1)rodata用來存放常量數據。 ro: read only

 2)字符串會被編譯器自動放在rodata中,加 const 關鍵字的常量數據會被放在 rodata 中

 3)在有的嵌入式系統中, rodata放在 ROM(或 NOR Flash)里,運行時直接讀取,不須加載到RAM內存中。

     所以,在嵌入式開發中,常將已知的常量系數,表格數據等造表加以 const 關鍵字。存在ROM中,避免占用RAM空間。

4.代碼(.text段)

  text段存放代碼(如:函數)和部分整數常量(應該指的是一些立即數),這個段是可執行的。

5.棧(stack)

  1)stack 存放函數的局部變量和函數參數

  2)被調用函數的參數和返回值 被存儲到當前程序的棧區,之后被調用函數再為自身的自動變量和臨時變量在棧區上分配空間

  3)函數返回時,棧區的數據會被釋放掉,先入后出(FILO)的順序。

6.堆(heap)

  heap用來動態分配內存,由程序自身決定開辟和釋放。

 6.1 malloc/free 

#include<stdio.h>
#include<stdlib.h>

int main(int argc, char *argv[])
{
    int *p = (int *)malloc(10*1);
    // p= (int *)malloc(10*1);
    if(p==NULL) {
        printf("malloc p err\n");
        return -1;
    }
    free(p);
    printf("p = %4x\n",p);
    p = NULL;
    printf("p = %4x\n",p);
    return 0;
}

 程序運行結果:

[hy@localhost memcfg]$ gcc maindata.c
[hy@localhost memcfg]$ ./a.out 
p = 9cf4008
p =    0

 1)開辟了空間,就要適時的釋放。釋放時,指針應指向開辟時的內存空間,所以在使用指針時,要注意不要修改了其地址,或者將開辟時的起始地址保存起來。

 2)對指針free后,其地址不一定就為NULL。如代碼中的 p,在 free(p)后,printf("p=%4x",p)后並非為0。所以建議在free(p)后,立即加一句p=NULL。

 3)檢查p的地址 if(p!=NULL){ ... }

6.2 calloc/realloc

 1)calloc()函數

void *calloc(size_t nmemb, size_t size);

   參數nmemb 表示要分配元素的個數,size表示每個元素的大小,分頻的內存空間大小是 nmemb*size; 返回值是 void* 類型的指針,指向分配好的內存首地址。

 用法一:分配1024*sizeof(int)字節大小的內存,並清空為0

int *p = (int *)calloc(1024,sizeof(int));

  用法二:與 alloc等價的 malloc 用法

int *p = (int *)malloc(1024*sizeof(int));
memset(p,0,1024*sizeof(int));

 差異:用法一calloc,會根據分配的的類型來初始化為0,如:分配int型,則初始化為(int)0; 若為指針類型,則初始化為空指針;若為浮點,則初始化為浮點型。

    用法二memset,不能保證初試化為空指針值和浮點型。(與NULL常量和浮點型的定義有關)

2)realloc()函數

  realloc()用來重新分配正在使用的一塊內存大小。

定義:

void *realloc(void *ptr, size_t size);

 用法示例:

int *p = (int *)malloc(1024);    //
p = (int *)realloc(512);        // 重新分配為 512字節大小內存,縮小數據丟失
p = (int *)realloc(2048);      // 重新分配為2048字節大小內存    

 注意:經過realloc()調整后的內存空間起始地址有可能與原來的不同。

摘錄書籍

《ARM嵌入式Linux系統開發詳解》 弓雷 等編著

《高質量嵌入式Linux C編程》 梁庚 等編著


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM