程序的內存分佈 - 以 Linux 爲例,基於 C 語言分析

這裏以 Linux 爲例,用 C 語言進行演示。html

內存模型

- 內存空間名稱 內容 讀寫操做 分配時機
高地址 kernel 內核空間 命令行參數、環境變量等 不可讀寫 程序運行時
- stack 棧空間 局部變量 可讀寫 程序運行時
- heap 堆空間 malloc() new() 內存分配函數建立 可讀寫 程序運行時
- 全局數據空間(初始化的和未初始化的) 靜態變量、全局變量 可讀寫 編譯時
- 只讀數據空間 程序的只讀數據(常量) 只讀 編譯時
低地址 代碼段 程序的機器碼,相同程序的多個運行實體之間共享 只讀 編譯時
  • 任何對代碼段的寫操做都會致使 segmentation fault 段錯誤
  • 常量、靜態變量、全局變量都是在編譯時分配內存空間

查看可執行文件的結構

size 查看可執行文件的內存分佈

能夠經過 size 命令查看可執行文件的內存分配,其中 text 的大小對應程序的只讀空間(代碼段和只讀數據段),data 對應初始化了的全局數據、靜態變量,bss 是未初始化數據段,包含未經初始化的全局變量和靜態變量。詳細例子能夠參考:https://blog.csdn.net/love_gaohz/article/details/50522447,簡單示例以下:linux

#include <stdio.h>

int b;
int main()
{
    int a = 888;
}

上面代碼中有未初始化的全局變量,編譯後用 size 查看:web

[root@VM_139_38_centos 20190121]# size build 
   text	   data	    bss	    dec	    hex	filename
   1127	    540	     12	   1679	    68f	build

修改 C 代碼,初始化全局變量:centos

#include <stdio.h>

int b = 666;
int main()
{
    int a = 888;
}

初始化全局變量後,編譯後用 size 查看:bash

[root@VM_139_38_centos 20190121]# size build 
   text	   data	    bss	    dec	    hex	filename
   1127	    544	      8	   1679	    68f	build

nm 查看可執行文件的標籤

# nm build 
000000000060102c D b
0000000000601030 B __bss_start
0000000000601030 b completed.6355
0000000000601028 D __data_start
0000000000601028 W data_start
0000000000400430 t deregister_tm_clones
00000000004004a0 t __do_global_dtors_aux
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
0000000000400588 R __dso_handle
0000000000600e28 d _DYNAMIC
0000000000601030 D _edata
0000000000601038 B _end
0000000000400574 T _fini
00000000004004c0 t frame_dummy
0000000000600e10 t __frame_dummy_init_array_entry
00000000004006b8 r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
00000000004003a8 T _init
0000000000600e18 t __init_array_end
0000000000600e10 t __init_array_start
0000000000400580 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000600e20 d __JCR_END__
0000000000600e20 d __JCR_LIST__
                 w _Jv_RegisterClasses
0000000000400570 T __libc_csu_fini
0000000000400500 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
00000000004004ed T main
0000000000400460 t register_tm_clones
0000000000400400 T _start
0000000000601030 D __TMC_END__

strings 查看可執行文件的常量

[root@VM_139_38_centos 20190121]# strings build 
/lib64/ld-linux-x86-64.so.2
Z%1X
libc.so.6
printf
__libc_start_main
__gmon_start__
GLIBC_2.2.5
UH-8
UH-8
[]A\A]A^A_
address: 
const is:%p
 global is %p
 local is %p
 function main is %p
;*3$"
...

下面的例子演示了各類類型變量常量的內存地址的位置:svg

#include <stdio.h>

const int a = 666;
int b = 777;
char * str = "hello\n";
int main()
{
	int c = 888;
	printf("address: \nconst is:%p\n global is %p\n local is %p\n function main is %p\n string str is %p", &a, &b, &c, main, &str);
	
	unsigned char * p = main;
	//p[0] = 0x0; // 這裏訪問只讀的代碼段會報錯
	str[3] = 'z'; // 這裏訪問只讀的代碼段會報錯
}

輸出爲:函數

address: 
const is:0x400600
 global is 0x601034
 local is 0x7ffdb921023c
 function main is 0x40052d
 string str is 0x601040

堆和棧的區別

只讀空間在程序運行以前就分配好了,運行結束後纔回收。ui

管理方式和分配方式不一樣

  • 程序的棧由編譯器自動管理。程序運行時每一個函數的變量放在棧 stack 中,函數返回時函數中的局部變量出棧釋放。
  • 程序的堆是動態分配的,由代碼控制。能夠經過 malloc() 和 free() 函數動態擴展和縮減(C++ 中對應 new() 和 delete()),malloc 能夠參考:http://www.javashuo.com/article/p-rqpuenba-gx.html
#include <stdio.h>
#include <stdlib.h>

int main()
{
	char *p = (char *)malloc(100);
	if (p == NULL) exit(1);
	int *a;
	a = (int *)malloc(sizeof(int));
	if (a == NULL) {
		free(p);
		exit(1);
	}
	free(a);
	printf("end");
}

碎片水平不一樣

  • 堆的分配和回收會形成內存空間的不連續,形成大量的碎片,使程序效率下降。
  • 棧不存在碎片問題,由於棧是先進後出的隊列,永遠不可能有一個內存塊從棧中間彈出。
相關文章
相關標籤/搜索