进程(程序)空间内存布局

It can be inferred that you lack confidence in a victory over your rivals from the fact that you are irritable against them. --------如果敌人让你生气,说明你还没有战胜他们的把握。

最近看了以前博客,觉得有点自导自演的感觉,讲述的知识点还是过于枯燥生硬,往后小黑会加入更多自己理解记忆过程中的想法,另外,因为我们程序员经常会和英语打交道,每篇博客开头会加入英语心灵鸡汤模块。一起进步一起学习。另外也希望码友多多指教。

标题中的(程序)其实是不准确的。 其实应该写成运行中的程序。
什么是一个进程?什么是程序?

  • 操作系统原理这么定义:正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进 程。站在程序员的角度来看,C文件叫做源码,源码给程序员来看的但机器不识别,这时我们需要使用 编译器编译生成CPU可识别的二进制可执行程序并保存在存储介质上,这时编译生成的可执行程序只能叫做程序而不能叫进程。
  • 而一旦程序开始运行了,那正在运行的这个程序及其占用的资源就叫做进程了。进程这个概念是针对系统, 而不是针对用户的,对用户来说,他面对的概念是程序。很显然,一个程序可以执行多次,这也意味着多个进程可以执行同一个程序。

1, 图示

其实写这篇博客是为后面的多进程编程打好基础。
Linux 进程内存管理的对象都是虚拟内存,每个进程先天就有 0-4G 的各自互不干涉的虚拟内存空间,0—3G 是用户空间执行用户自己的代码, 高 1GB 的空间是内核 空间执行 Linu x 系统调用,这里存放在整个内核的代码和所有的内核模块,用户所看到和接触的都是该虚拟地址,并不是实际 的物理内存地址。

  • Linux下一个进程在内存里有三部分的数据,就是”代码段”、”堆栈段”和”数据段”。
    代码段: 顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。
    堆栈段:存放的就是子程 序的返回地址、子程序的参数以及程序的局部变量和malloc()动态申请内存的地址。
    数据段: 存放程序的全局变量,静态变量及常量的内存空间。
    在这里插入图片描述

  • 注意,图中的段区不要计较,这个是根据每个人的叫法习惯而定的。

2, 具体解释

Linux 内存管理的基本思想就是只有在真正访问一个地址的时候才建立这个地址的物理映射,Linux C/C++语言的分配方式共有 3 种方式。
(1)从静态存储区域分配。就是数据段的内存分配,这段内存在程序编译阶段就已经分配好,在程序的整个运行期间都存在, 例如全局变量,static 变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。 栈内存分配运算内置于处理器的指令集中,效率很高,但是系统栈中分配的内存容量有限,比如大额数组就会把栈空间撑爆导致
段错误。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。此 区域内存分配称之为动态内存分配。动态内存的生存期由我们决定,使用非常灵活,但问题也 多,比如指向某个内存块的指针取值发生了变化又没有其他指针指向 这块内存,这块内存就无法访问,发生内存泄露。

各层 具体介绍
栈区 栈内存由编译器在程序编译阶段完成,进程的栈空间位于进程用户空间的顶部并且是向下增长,每个函数的每次调用 都会在栈空间中开辟自己的栈空间,函数参数、局部变量、函数返回地址等. 都会按照先入者为栈顶的顺序压入函数栈中, 函数返回后该函数的栈空间消失,所以函数中返回局部变量的地址都是非法的。
堆区 堆内存是在程序执行过程中分配的,用于存放进程运行中被动态分配的的变量,大小并不固定,堆位于非初始化数据 段和栈之间,并且使用过程中是向栈空间靠近的。当进程调用 malloc 等函数分配内存时,新分配的内存并不是该函数的 栈帧中,而是被动态添加到堆上,此时堆就向高地址扩张;当利用 free 等函数释放内存时,被释放的内存从堆中被踢 出,堆就会缩减。因为动态分配的内存并不在函数栈帧中,所以即使函数返回这段内存也是不会消失。
非初始化数据段(bss段) 用来存放未初始化全局变量和 static 静态变量。并且在程序开始执行之前, 就是在 main()之前,内核会将此段中的数据初始化为 0 或空指针。程序结束后静态变量资源由系统自动释放。静态分配
初始化数据段(.data)有些地方会直接叫成数据段 也属于静态内存分配,用来保已初始化全局变量和 static 静态变量 ,内存区域属于只读。
文字常量段(.rodata) 看上图
文本段 这是可执行文件中由 CPU 执行的机器指令部分。正文段常常是只读的,以防止程序由于意外而修改 其自身的执行。
  • 下面几点需要注意

  • 未运行的程序本质上都是由BSS段、data段、text段三个组成的。

  • BSS段并不占用可执行文件的大小,它是由链接器来获取内存的。

  • bss段(未进行初始化的数据)的内容并不存放在磁盘上的程序文件中。其原因是内核在程序开始运行前将它们设置为0。

  • 需要存放在程序文件中的只有正文段和初始化数据段。

  • BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段的后面。当这个内存进入程序的地址空间后全部清零。

  • 栈区:栈区是从高地址位向低地址位增长的,是一块连续的内存区域. 分配的栈区大小一般较小。

  • 堆区:用于动态分配内存,由程序员申请分配和释放。堆是从低地址位向高地址位增长,采用链式存储结构。频繁的malloc/free造成内存空间的不连续,产生碎片。当申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。

3, 代码验证

下面使用c代码验证上面的进程内存空间是否正确。

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


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


int main(int argc, char **argv) //argv里存放的是命令行参数,他存放在命令行参数区
{ 
    static int   local_bss_var;     // 未初始化的静态变量,存放在数据段的BSS区,其值默认为0;  
    


    static int   local_data_var = 10;  // 初始化了的静态变量,存放在数据段的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 ]:  local_bss_var address: %p value: %d\n", &local_bss_var, local_bss_var);      

    printf("[  bss ]:  global_bss_var address: %p value: %d\n", &global_bss_var, global_bss_var);
    

    printf("[ data ]:  local_data_var address: %p value: %d\n", &local_data_var, local_data_var);
   
    printf("[ data ]:  global_data_var address: %p value: %d\n", &global_data_var, global_data_var);       
    
    printf("[rodata]: \"%s\" address: %p \n", str, str);   
    printf("--------\n");
    
    printf("[ text ]:  main() address: %p\n", main); 
    printf("--------\n");
    
    return 0;

}

在这里插入图片描述

发布了29 篇原创文章 · 获赞 65 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_46027505/article/details/105076010