Linux下的进程内存布局 / c程序内存布局

进程空间内存布局

不管学习c语言还是学习linux下的编程,我们都要了解并学习。每个进程先天就有0-4G的各自互不干涉的虚拟内存空间,0-3G是用户空间执行用户自己的代码,高1GB的空间是内核空间执行linux系统调用。这里存放着整个内核的代码和所有的内核模块,用户所看到和接触的都是该虚拟地址,并不是实际的物理地址。linux下一个进程在内存里有三部分的数据,就是“代码段”、“堆栈段”和“数据段”。
下面我们来看一下linux下进程的内存布局
在这里插入图片描述

上述图片文字解析

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

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

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

4、已初始化数据段(.data): 通常将已初始化数据是在程序中声明,并且具有初值的变量,这些变量需要占用存储器的空间,在程序执行时它们需要位于可读写的内存区域内,并具有初值,以供程序运行时读写。一般用来已初始化的全局变量和 static 静态变量。

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

内存布局编程代码

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

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

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

int g_var2; //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是未初始化了的局部变量,存放在栈中,其值为随机值,这时候的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;
}

代码运行结果
在这里插入图片描述
结果分析:通过上面代码的地址,我们可以发现,在内存中,从低地址向高地址,依次是文本段、文字常量段、已初始化数据段、未初始化数据段、堆区域和栈区域。(对照上面内存布局图片看)

发布了10 篇原创文章 · 获赞 44 · 访问量 6715

猜你喜欢

转载自blog.csdn.net/makunIT/article/details/104685261