linux内存分布

Linux下进程的内存布局

布局分析

一般Linux 进程内存管理的对象都是虚拟内存,每个进程先天就有 0-4G 的各自互不干涉的虚拟内存空间,0—3G 是用户空间执行用户自己的代码(即参数区,堆栈区,数据段与文本段), 高 1GB 的空间是内核空间执行 Linu x 系统调用(即System Space)。

如该内存图片
在这里插入l图片描述

详细分析:

文本段(代码段):

代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。,这是可执行文件中由 CPU 执行的机器指令部分。正文段常常是只读的,以防止程序由于意外而修改其自身的执行。代码编译后的机器指令经常被放在代码段里,代码段名为".text";

数据段:

bss段:

使用方式:
未初始化的全局变量和静态局部变量
初始值为0的全局变量和静态局部变量(依赖于编译器实现)
未定义且初值不为0的符号(该初值即common block的大小)

 int    g_var1;
 int    g_var2 = 0;
 {
    static int   s_var1;
    static int   s_var2 = 0;
 }

g_var1,g_var2, s_var1,s_var2均位于bss区。

data段

使用方式:
已初始化且初值不为0的全局变量和静态局部变量

 int    g_var3 = 20;
 {
 
    static int   s_var3= 10;
 }

g_var3,s_var3均位于data段

DATA与BSS段的区别:
 BSS段不占用物理文件尺寸,但占用内存空间;数据段占用物理文件,也占用内存空间。

 对于大型数组如int ar0[10000] = {1, 2, 3, ...}和int ar1[10000],ar1放在BSS段,只记录共有10000*4个字节需要初始化为0,而不是像ar0那样记录每个数据1、2、3...,此时BSS为目标文件所节省的磁盘空间相当可观。
文字常量区(.rodata)
char *c = "HELLO";

此时hello存储在rodata区

堆区

堆内存是在程序执行过程中分配的,用于存放进程运行中被动态分配的的变量,大小并不固定,堆位于非初始化数据段和栈之间,使用时需要程序员用malloc 等函数分配内存,结束后需要free等函数释放内存。当进程调用 malloc 等函数分配内存时,新分配的内存并不是该函数的栈帧中,而是被动态添加到堆上,此时堆就向高地址扩张。(从下至上)

栈区

栈内存由编译器在程序编译阶段完成,由系统自动进行分配即释放,进程的栈空间位于进程用户空间的顶部并且是向下增长动态增长(从上至下),每个函数的每次调用都会在栈空间中开辟自己的栈空间,函数参数、局部变量、函数返回地址等都会按照先入者为栈顶的顺序压入函数栈中,函数返回后该函数的栈空间消失,所以函数中返回局部变量的地址都是非法的。

堆和栈的区别

栈和堆的区别

①管理方式:栈由编译器自动管理;堆由程序员控制,使用方便,但易产生内存泄露。

②生长方向:栈向低地址扩展(即”向下生长”),是连续的内存区域;堆向高地址扩展(即”向上生长”),是不连续的内存区域。这是由于系统用链表来存储空闲内存地址,自然不连续,而链表从低地址向高地址遍历。

③空间大小:栈顶地址和栈的最大容量由系统预先规定(通常默认2M或10M);堆的大小则受限于计算机系统中有效的虚拟内存,32位Linux系统中堆内存可达2.9G空间。

④存储内容:栈在函数调用时,首先压入主调函数中下条指令(函数调用语句的下条可执行语句)的地址,然后是函数实参,然后是被调函数的局部变量。本次调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点继续运行下条可执行语句。堆通常在头部用一个字节存放其大小,堆用于存储生存期与函数调用无关的数据,具体内容由程序员安排。

⑤分配方式:栈可静态分配或动态分配。静态分配由编译器完成,如局部变量的分配。动态分配由alloca函数在栈上申请空间,用完后自动释放。堆只能动态分配且手工释放。

⑥分配效率:栈由计算机底层提供支持:分配专门的寄存器存放栈地址,压栈出栈由专门的指令执行,因此效率较高。堆由函数库提供,机制复杂,效率比栈低得多。Windows系统中VirtualAlloc可直接在进程地址空间中分配一块内存,快速且灵活。

⑦分配后系统响应:只要栈剩余空间大于所申请空间,系统将为程序提供内存,否则报告异常提示栈溢出。

操作系统为堆维护一个记录空闲内存地址的链表。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增加程序数据段的内存空间,以便有机会分到足够大小的内存,然后进行返回。,大多数系统会在该内存空间首地址处记录本次分配的内存大小,供后续的释放函数(如free/delete)正确释放本内存空间。

此外,由于找到的堆结点大小不一定正好等于申请的大小,系统会自动将多余的部分重新放入空闲链表中。

⑧碎片问题:栈不会存在碎片问题,因为栈是先进后出的队列,内存块弹出栈之前,在其上面的后进的栈内容已弹出。而频繁申请释放操作会造成堆内存空间的不连续,从而造成大量碎片,使程序效率降低。

可见,堆容易造成内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和内核态切换,内存申请的代价更为昂贵。所以栈在程序中应用最广泛,函数调用也利用栈来完成,调用过程中的参数、返回地址、栈基指针和局部变量等都采用栈的方式存放。所以,建议尽量使用栈,仅在分配大量或大块内存空间时使用堆。

使用栈和堆时应避免越界发生,否则可能程序崩溃或破坏程序堆、栈结构,产生意想不到的后果。

参数区

用于储存系统命令,命令行产生的参数,

int main(int argc, char **argv)

argv里存放的是命令行参数,他存放在命令行参数区。

System Space:

linux运行的系统空间

全部程序代码分析

#include <stdio.h>
#include <stdlib.h>
int g_var1; // g_var1是未初始化的全局变量,存放在数据段的BSS区,其值默认为0;
int g_var2=20; // g_var1是初始化了的全局变量,存放在数据段的DATA区,其值为初始化值20;
int main(int argc, char **argv) //argv里存放的是命令行参数,他存放在命令行参数区
{
 static int s_var1; // s_var1是未初始化的静态变量,存放在数据段的BSS区,其值默认为0;
 static int s_var2=10; // g_var1是初始化了的静态变量,存放在数据段的DATA区,其值为初始化值10;
 char *str="Hello"; // str是初始化了的局部变量,存放在栈(STACK)中,其值是"Hello"这个字符串常量存放
在DATA段里RODATA区中的地址
 char *ptr; // ptr是未初始化了的局部变量,存放在栈(STACK)中;其值为随机值,这时候的ptr称 为“野指针(为初始化的指针)”
 ptr = malloc(100); // malloc()会从堆(HEAP)中分配100个字节的内存空间,并将该内存空间的首地址返回给ptr存 放;
 printf("[cmd args]: argv address: %p\n", argv);
 printf("\n");
 printf("[ Stack]: str address: %p\n", &str);
 printf("[ Stack]: ptr address: %p\n", &ptr);
 printf("\n");
 printf("[ Heap ]: malloc address: %p\n", ptr);
 printf("\n");
 printf("[ bss ]: s_var1 address: %p value: %d\n", &s_var1, g_var1);
 printf("[ bss ]: g_var1 address: %p value: %d\n", &g_var1, g_var1);
 printf("[ data ]: g_var2 address: %p value: %d\n", &g_var2, g_var2);
 printf("[ data ]: s_var2 address: %p value: %d\n", &s_var2, s_var2);
 printf("[rodata]: \"%s\" address: %p \n", str, str);
 printf("\n");
 printf("[ text ]: main() address: %p\n", main);
 printf("\n");
 return 0;
}

运行结果

[cmd args]: argv address: 0x7eac9384
[ Stack]: str address: 0x7eac9224
[ Stack]: ptr address: 0x7eac9220
[ Heap ]: malloc address: 0xb34008
[ bss ]: s_var1 address: 0x2103c value: 0
[ bss ]: g_var1 address: 0x21040 value: 0
[ data ]: g_var2 address: 0x21030 value: 20
[ data ]: s_var2 address: 0x21034 value: 10
[rodata]: “Hello” address: 0x10670
[ text ]: main() address: 0x104a0
从地址可以看出符合上面图片的布局。

文章参考:
https://blog.csdn.net/u010974016/article/details/114188998?ops_request_misc=&request_id=&biz_id=102&utm_term=linux%E6%96%87%E6%9C%AC%E6%AE%B5%E5%AD%98%E5%82%A8%E5%86%85%E5%AE%B9&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-3-114188998.pc_search_result_no_baidu_js

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值