Linux学习之内存空间布局

Linux进程内存管理的对象都是虚拟内存,每个进程先天都有0-4G的各自互不干涉的虚拟内存空间,0-3G是用户空间执行用户自己的代码,高1GB的空间是内核空间执行Linux系统调用,这里存放着整个内核的代码和所有的内核模块,用户所看到和接触的都是该虚拟地址,并不是实际的物理内存地址。Linux下一个进程在内存里有三部分的数据,就是“代码段”、“堆栈段”和“数据段”。代码段就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。堆栈段存放的就是子程序的返回地址、子程序的参数以及程序的局部变量和malloc()动态申请内存的地址。而数据段则存放程序的全局变量,静态变量及常量的内存空间。

Linux学习之内存空间布局_第1张图片

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

2、堆:堆内存是在程序执行过程中分配的,用于存放进程运行中被动态分配的变量,大小并不固定,堆位于非初始化数据段和栈之间,并且使用过程中是向栈空间靠近的。当进程调用malloc等函数分配内存时,新分配的内存并不是该函数的栈帧中,而是被动态添加到堆上,此时堆就向高地址扩张;当利用free等函数释放内存时,被释放的内存从堆中被踢出,堆就会缩减。因为动态分配的内存并不在函数栈帧中,所以即使函数返回这段内存也是不会消失。

3、非初始化数据段:通常将此段称为bss段,用来存放未初始化的全局变量和static静态变量。并且在程序开始执行之前,就是在main()之前,内核会将此段中的数据初始化为0或空指针。

4、初始化数据段:用来保存已初始化的全局变量和static静态变量。

5、文本段也称代码段:这是可执行文件中由CPU执行的机器指令部分。正文段常常是只读的,以防止程序由于意外而修改其自身的本身的执行。

Linux内存管理的基本思想就是只有在真正访问一个地址的时候才建立这个地址的物理映射,Linux C/C++ 语言的分配方式共有3种方式。

(1)从静态存储区域分配。就是数据段的内存分配,这段内存在程序编译阶段就已经分配好,在程序的整个运行期间都存在,例如全局变量,static变量。

(2)在栈上创建。在执行函数时,函数内部局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是系统栈中分配的内存容量有限,比如大数额组就会把栈空间撑爆导致段错误。

(3)从堆上分配。亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。此区域内存分配称之为动态内存分配。动态内存的生存期由我们决定,使用非常灵活,但问题也最多,比如指向某个内存块的指针取值发生了变化又没有其他指针指向这块内存,这块内存就无法访问,发生内存泄漏。

接下来我们编写并运行下面C程序,验证一下C程序编译以及运行时的进程内存布局:

#include 
#include 



int             g_var1;                 //g_var1是未初始化的全局变量,存放在数据段的BSS区,其值默认为0
int             g_var2 = 20;            //g_var2是初始化了的全局变量,存放在数据段的DATA区,其值为初始化值20




int main(int argc,char *argv[])
{

        static  int             s_var1;                 //s_var1是未初始化的静态变量,存放在数据段的数据段的BSS区,其值默认为0
        static  int             s_var2 = 10;            //s_var2是初始化了的静态变量,存放在数据段的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,s_var1);
        printf("[bss] : g_var1 address:%p value:%d\n",&g_var1,g_var1);

        printf("[data] : s_var2 address:%p value:%d\n",&s_var2,s_var2);
        printf("[data] : g_var2 address:%p value:%d\n",&g_var2,g_var2);

        printf("[rodata] : \"%s\" address:%p\n",str,str);
        printf("\n");

        printf("[text]:main() address :%p\n",main);
        printf("\n");

        return 0;

}

运行结果:

Linux学习之内存空间布局_第2张图片

通过运行结果,再结合上面的知识点,相信大家一定能有所收获,谢谢大家!

你可能感兴趣的:(C语言,Linux,APUE,linux,学习,运维)