linux进程内存布局

一个程序本质上都是由 BSS 段、data段、text段三个组成的。这样的概念在当前的计算机程序设计中是很重要的一个基本概念,而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题。

  • BSS段:在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
  • 数据段:在采用段式内存管理的架构中,数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
  • 代码段:在采用段式内存管理的架构中,代码段(text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

程序编译后生成的目标文件至少含有这三个段,这三个段的大致结构图如下所示:

其中.text即为代码段,为只读。.bss段包含程序中未初始化的全局变量和static变量。data段包含三个部分:heap(堆)、stack(栈)和静态数据区。

  • 堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
  • 栈 (stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变 量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以 栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

当程序在执行时动态分配空间(C中的malloc函数),所分配的空间就属于heap。其概念与数据结构中“堆”的概念不同。

stack段存放函数内部的变量、参数和返回地址,其在函数被调用时自动分配,访问方式就是标准栈中的LIFO方式。(因为函数的局部变量存放在此,因此其访问方式应该是栈指针加偏移的方式,否则若通过push、pop操作来访问相当麻烦)

data段中的静态数据区存放的是程序中已初始化的全局变量、静态变量和常量。

在采用段式内存管理的架构中(比如intel的80x86系统),BSS 段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时 BSS 段部分将会清零。BSS 段属于静态内存分配,即程序一开始就将其清零了。

比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。

text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而BSS段不在可执行文件中,由系统初始化。

图引自《C专家编程》

BSS段只保存没有值的变量,所以事实上它并不需要保存这些变量的映像。运行时所需要的BSS段大小记录在目标文件中,但BSS段并不占据目标文件的任何空间。

    1. //main.c
    2. int a = 0; //全局初始化区
    3. char *p1; //全局未初始化区
    4. main()
    5. {
    6. static int c =0; //全局(静态)初始化区
    7. int b; //栈
    8. char s[] = "abc"; //栈
    9. char *p2; //栈
    10. char *p3 = "123456"; //"123456\0"在常量区,p3在栈上。
    11. p1 = (char *)malloc(10);
    12. p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。
    13. }

(图出自:http://www.tenouk.com/Bufferoverflowc/Bufferoverflow1c.html

(图出自:APUE-2e, http://infohost.nmt.edu/~eweiss/222_book/222_book.html

The computer program memory is organized into the following:

Code segment(text segment)
Data Segment 
-- Data (rodata + rwdata)
-- BSS
-- Heap
Stack Segment

Data

The data area contains global and staticvariables used by the program that are initialized. This segment can be furtherclassified into initialized read-only (rodata) area and initialized read-writearea (rwdata).

BSS

The BSS segment also known as uninitialized datastarts at the end of the data segment and contains all uninitialized globalvariables and static variables that are initialized to zero by default.

Heap

The heap area begins at the end of the BSSsegment and grows to larger addresses from there. The heap area is managed bymalloc/calloc/realloc/new and free/delete, which may use the brk and  sbrk system calls to adjust its size. The heaparea is shared by all shared libraries and dynamically loaded modules in aprocess.

Stack

The stack is a LIFO structure, typically locatedin the higher parts of memory. It usually "grows down" with everyregister, immediate value or stack frame being added to it. A stack frameconsists at minimum of a return address

例子程序

  1. //main.cpp

  2.  

    int a = 0; // 全局初始化区(data)

  3.  

    char *p1; // 全局未初始化区(bss)

  4.  

    int main()

  5.  

    {

  6.  

    int b; // 栈区(stack)

  7.  

    char s[] = "abc"; // 栈区(stack)

  8.  

    char *p2; // 栈区(stack)

  9.  

    char *p3 = "123456"; // p3 在栈区(stack); "123456\0" 在常量区(rodata)

  10.  

    static int c =0; // 全局/静态 初始化区 (data)

  11.  

    p1 = (char *)malloc(10);

  12.  

    p2 = (char *)malloc(20); // 分配得来的 10 和 20 字节的区域就在堆区 (heap)

  13.  

    strcpy(p1, "123456"); // "123456\0" 放在常量区(rodata). 编译器可能会将它与 p3 所指向的"123456\0"优化成一个地方。

  14.  

    return 0;

  15.  

堆和栈的区别

管理方式:对于栈来讲,是由编译器自动管理;对于堆来说,释放工作由程序员控制,容易产生 memory leak。

空间大小:一般来讲在 32 位系统下,堆内存可以达到接近 4G 的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在 VC6 下面,默认的栈空间大小大约是 1M。

碎片问题:对于堆来讲,频繁的new/delete 势必会造成内存空间的不连续,从而造成大量碎片,使程序效率降低;对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,永远都不可能有一个内存块从栈中间弹出。

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

分配方式:堆都是动态分配的,没有静态分配的堆;栈有 2 种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配,动态分配由 alloca 函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,不需要我们手工实现。

分配效率:栈是机器系统提供的数据结构,计算机会在底层分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高;  堆则是 C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,然后进行返回。显然,堆的效率比栈要低得多。

无论是堆还是栈,都要防止越界现象的发生。

关于 Global 和 Static 类型的一点讨论

1. static 全局变量与普通的全局变量有什么区别 ?

全局变量(外部变量)的定义之前再冠以 static 就构成了静态的全局变量。

全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。

这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。  而静态全局变量则限制了其作用域,  即只在定义该变量的源文件内有效,  在同一源程序的其它源文件中不能使用它。

由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

static 全局变量只初使化一次,防止在其他文件单元中被引用。

2. static 局部变量和普通局部变量有什么区别 ?

把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

static 局部变量只被初始化一次,下一次依据上一次结果值。

3. static 函数与普通函数有什么区别?

static 函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.static 函数在内存中只有一份(.data),普通函数在每个被调用中维持一份拷贝。

原文地址:https://www.cnblogs.com/sunsky303/p/9251819.html

时间: 2024-08-25 10:03:12

linux进程内存布局的相关文章

Linux C进程内存布局

当程序文件运行为进程时,进程在内存中获得空间.这个空间是进程自己的内存空间.每个进程空间按照如下方式分为不同区域: 进程内存空间布局图 text:代码段.存放的是程序的全部代码(指令),来源于二进制可执行文件中的代码部分 initialized data(简称data段)和uninitialized data(简称bss段)组成了数据段.其中data段存放的是已初始化全局变量和已初始化static局部变量,来源于二进制可执行文件中的数据部分: bss段存放的是未初始化全局变量和未初始化stati

一个由进程内存布局异常引起的问题

一个由进程内存布局异常引起的问题 前段时间业务反映某类服务器上更新了 bash 之后,ssh 连上去偶发登陆失败,客户端吐出错误信息如下所示:图 - 0 该版本 bash 为部门这边所定制,但实现上并没有改动原有逻辑,只是加入了些监控功能,那么这些错误从哪里来呢? 是 bash 的锅吗 从上面的错误信息可以猜测,异常是 bash 在启动过程中分配内存失败所导致,看起来像是某些情况下该进程错误地进行了大量内存分配,最后导致内存不足,要确认这个事情比较简单,动态内存分配到系统调用这一层上主要就两种方

linux下进程内存布局及变量存储位置检查

进程的内存布局如下(虚拟内存): 它们分别从低地址向高地址增长 在linux中,存在三个全局符号:etext, edata, end分别指向文本段,初始化数据段,未初始化数据段结尾处的下一字节的地址. 所以我们可以在c程序中声明这些变量,然后定义一些变量再查看其地址是否在对应的地址范围内,可得出其变量被存储在哪个区中. #include<unistd.h> #include<stdlib.h> #include<stdio.h> extern char etext, e

查看LINUX进程内存占用情况

可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令: (1)top top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器 可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令: $ top -u oracle 内容解释: PID:进程的ID USE

linux进程内存到底怎么看 剖析top命令显示的VIRT RES SHR值

引 言: top命令作为Linux下最常用的性能分析工具之一,可以监控.收集进程的CPU.IO.内存使用情况.比如我们可以通过top命令获得一个进程使用了多少虚拟内存(VIRT).物理内存(RES).共享内存(SHR). 最近遇到一个咨询问题,某产品做性能分析需要获取进程占用物理内存的实际大小(不包括和其他进程共享的部分),看似很简单的问题,但经过研究分析后,发现背后有很多故事-- 1 VIRT RES SHR的准确含义 三个内存指标,VRIT,RES,SHR准确含义是什么?谁能告诉我们?MAN

Linux进程内存统计

一. 进程内存统计 cat /proc/[pid]/status通过/proc/[pid]/status可以查看进程的内存使用情况,包括虚拟内存大小(VmSize),物理内存大小(VmRSS),数据段大小(VmData),栈的大小(VmStk),代码段的大小(VmExe),共享库的代码段大小(VmLib)等等. Name: java /进程的程序名/ State: S (sleeping) /进程的状态信息,具体参见/ Tgid: 9744 /线程组号/ Pid: 9744 /进程pid/ PP

Unix系统编程()进程内存布局

每个进程所分配的内存由很多部分组成,通常称之为"段(segment)". 文本段包含了进程运行的程序机器语言指令.文本段具有只读属性,以防止进程通过错误指针意外修改自身指令. 因为多个进程可同时运行同一程序,所以又将文本段设为可共享,这样,一份程序代码的拷贝可以映射到所有这些进程的虚拟地址空间中. 初始化数据段包含显式初始化的全局变量和静态变量.当程序加载到内存时,从可执行文件中读取这些变量的值. 未初始化数据段包括了未进行显式初始化的全局变量和静态变量. 程序启动之前,系统将本段内所

c进程内存布局说明

Text:代码段.存放程序的全部代码(指令),来源于二进制可执行文件中的代码部分,在编译的时候就已经放置到二进制文件中. Initialized data(简称data段)和uninitialized data(简称bss段)组成了数据段.其中data段存放的是已初始化全局变量和已初始化static局部变量,来源于二进制可执行文件中的数据部分,这部分在编译的时候就已经放置到二进制文件中:bss段存放的是未初始化全局变量和未初始化static局部变量,其内容不来源于二进制可执行文件中的数据部分(也

Linux 可执行文件与进程内存结构, Linux 进程内存加载

一个可执行程序包含三个部分 代码段:主要存放指令,操作以及只读的(常量)数据(例如字符串常量).数据段:全局或者静态的已经初始化的变量.BSS 段:全局或者静态的未初始化的变量. 栈上面有1G内存时Linux内核区,与栈之间有一个gap随机地址,防止代码攻击.数据区data与堆区之间也有一个随机gap.