Linux段管理,BSS段,data段,.rodata段,text段


         近期在解決一個編譯問題時,一直在考慮一個問題,那就是Linux下可執行程序執行時內存是什么狀態,是依照什么方式分配內存並執行的。查看了一下資料。就此總結一下,眾所周知。linux下內存管理是通過虛存管理的,在分配內存是並不是在物理內存開辟了一段空間,而是在使用時才分配的。並且是通過段頁式管理。

以上比較廢話,開始看看程序執行時內存會是什么狀態。

        在linux下內存分配是以頁為單位的。而頁是通過段管理。各個段之間是獨立的,方便管理。linux程序執行時。能夠分為下面幾個內存段:

一、BSS段 bss segment)一般是指用來存放程序中未初始化的全局變量的一塊內存區域。

BSS是英文Block Started by Symbol的簡稱。

BSS段屬於靜態內存分配。

    該段用於存儲未初始化的全局變量或者是默認初始化為0的全局變量,它不占用程序文件的大小,可是占用程序執行時的內存空間。

#define DEBUG "debug"

int space[1024][1024];

int main()
{
  char *a = DEBUG;
  return 1;
}

上面聲明了一個space的二維數組,是一個全局變量,沒有被初始化。通過nm命令能夠查看程序中的符號信息例如以下:

0000000000600660 d _DYNAMIC
00000000006007f8 d _GLOBAL_OFFSET_TABLE_
0000000000400578 R _IO_stdin_used
                 w _Jv_RegisterClasses
0000000000600640 d __CTOR_END__
0000000000600638 d __CTOR_LIST__
0000000000600650 D __DTOR_END__
0000000000600648 d __DTOR_LIST__
0000000000400630 r __FRAME_END__
0000000000600658 d __JCR_END__
0000000000600658 d __JCR_LIST__
000000000060081c A __bss_start
0000000000600818 D __data_start
0000000000400530 t __do_global_ctors_aux
00000000004003e0 t __do_global_dtors_aux
0000000000400580 R __dso_handle
                 w __gmon_start__
0000000000600634 d __init_array_end
0000000000600634 d __init_array_start
0000000000400490 T __libc_csu_fini
00000000004004a0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
000000000060081c A _edata
0000000000a00840 A _end
0000000000400568 T _fini
0000000000400358 T _init
0000000000400390 T _start
00000000004003bc t call_gmon_start
0000000000600820 b completed.6347
0000000000600818 W data_start
0000000000600828 b dtor_idx.6349
0000000000400450 t frame_dummy
0000000000400474 T main
0000000000600840 B space

最后一行的B表示是BSS段。也就表示space是存在於BSS段中的。

二、data段 該段用於存儲初始化的全局變量,初始化為0的全局變量出於編譯優化的策略還是被保存在BSS段,對上面的程序做一下更改就能夠看到是怎樣分配的了。

#define DEBUG "debug"

int space[1024][1024];
int data = 1;
int no_data = 0;

int main()
{
  char *a = DEBUG;
  return 1;
}

使用nm查看后

0000000000600660 d _DYNAMIC
00000000006007f8 d _GLOBAL_OFFSET_TABLE_
0000000000400578 R _IO_stdin_used
                 w _Jv_RegisterClasses
0000000000600640 d __CTOR_END__
0000000000600638 d __CTOR_LIST__
0000000000600650 D __DTOR_END__
0000000000600648 d __DTOR_LIST__
0000000000400630 r __FRAME_END__
0000000000600658 d __JCR_END__
0000000000600658 d __JCR_LIST__
0000000000600820 A __bss_start
0000000000600818 D __data_start
0000000000400530 t __do_global_ctors_aux
00000000004003e0 t __do_global_dtors_aux
0000000000400580 R __dso_handle
                 w __gmon_start__
0000000000600634 d __init_array_end
0000000000600634 d __init_array_start
0000000000400490 T __libc_csu_fini
00000000004004a0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000600820 A _edata
0000000000a00840 A _end
0000000000400568 T _fini
0000000000400358 T _init
0000000000400390 T _start
00000000004003bc t call_gmon_start
0000000000600820 b completed.6347
000000000060081c D data
0000000000600818 W data_start
0000000000600828 b dtor_idx.6349
0000000000400450 t frame_dummy
0000000000400474 T main
0000000000600830 B no_data
0000000000600840 B space

能夠看到變量data被分配在data段,而被初始化為0的no_data被分配在了BSS段。

三、.rodata段

該段也叫常量區,用於存放常量數據,ro就是Read Only之意。

可是注意並非全部的常量都是放在常量數據段的。其特殊情況例如以下:
1)有些馬上數與指令編譯在一起直接放在代碼段。

int main()
{
  int a = 10;
  return 1;
}
a是常量。可是它沒有被放入常量區,而是在指令中直接通過馬上數賦值



2)對於字符串常量,編譯器會去掉反復的常量,讓程序的每一個字符串常量僅僅有一份。

char *str = "123456789";
char *str1 = "helloworld";

int main()
{
  char* a = "helloworld";
  char b[10] = "helloworld";
  return 1;
}
匯編代碼例如以下:

                .file   "hello.c"
.globl str
        .section        .rodata
.LC0:
        .string "123456789"
        .data
        .align 8
        .type   str, @object
        .size   str, 8
str:
        .quad   .LC0
.globl str1
        .section        .rodata
.LC1:
        .string "helloworld"
        .data
        .align 8
        .type   str1, @object
        .size   str1, 8
str1:
        .quad   .LC1
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    $.LC1, -8(%rbp)
        movl    $1819043176, -32(%rbp)
        movl    $1919907695, -28(%rbp)
        movw    $25708, -24(%rbp)
        movl    $1, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
        .section        .note.GNU-stack,"",@progbits
能夠看到str1和a同一時候指向.rodata段中同一個LC1,而是用數組初始化的字符串常量是沒有放入常量區的,另外用const修飾的全局變量是放入常量區的,可是使用cons修飾的局部變量僅僅是設置為僅僅讀起到防止改動的效果,沒有放入常量區。

3)有些系統中rodata段是多個進程共享的,目的是為了提高空間的利用率。

四、text段

text段是用於存放程序代碼的,編譯時確定,僅僅讀。更進一步講是存放處理器的機器指令,當各個源文件單獨編譯之后生成目標文件。經連接器鏈接各個目標文件並解決各個源文件之間函數的引用,與此同一時候,還得將全部目標文件里的.text段合在一起,但不是簡單的將它們“堆”在一起就完事,還須要處理各個段之間的函數引用問題。

五、stack段

也就是棧段,常說的堆棧段之中的一個,是由系統負責申請釋放,其操作方式類似stack,用於存儲參數變量及局部變量,事實上函數的運行也是stack的方式,所以才有了遞歸

六、heap段

也就是俗稱的堆。它由用戶申請和釋放。申請時至少分配虛存,當真正存儲數據時才分配對應的實存,釋放時也並不是馬上釋放實存。而是可能被反復利用。待興許會再細致介紹相關的知識。


能夠看到堆和棧的內存增長方向是相反的。興許會對linux的內存管理做具體的介紹



免責聲明!

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



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