当前位置: 首页 > Linux

linux进程地址空间布局分析

时间:2023-04-06 21:25:37 Linux

这篇文章是围绕本文思路做的一个总结,原文章记录的笔记很详细,在此基础上,我自己总结一下(我想简单点)。如果您想了解具体细节,可以访问这篇文章。文末你交了一段代码,还有程序的内存映射。如有不妥之处,请补充指正。什么是程序抽象地说,当我们打开电脑,点击应用程序图标时,就会运行一个程序,但在操作系统层面,实际上会生成一个进程,它是程序的实体。接下来以linux操作系统来介绍linux中进程的各个段以及它们代表的含义linux虚拟地址空间在linux操作系统中,每当一个进程产生时,它都有一个虚拟地址空间,通常在32位位系统中,其大小为4GB,按3:1的比例分配给用户空间和内核空间。虚拟地址空间的简单工作原理就是通过页表将虚拟地址映射到实际的物理地址(所以在C语言中malloc分配内存时,即使malloc有int大小的空间,实际分配的还是一个page内存,一般为4096字节)用户内存空间的每一段分布代码段:可执行代码、字符串常量和其他数据段:1.数据段:初始化的全局变量,初始值不为0(静态变量)2.bss段:未初始化的,或者初始值为0的全局变量(静态变量)Heap:可以理解为程序员为程序中的变量分配的内存(需要手动释放)Stack:保存的时候函数被调用所需的局部变量、函数参数和返回地址。当进程产生很多线程时,线程堆栈也保存在这里。说到底,栈也是一种数据结构。它遵循先进后出的顺序,所以特别适合函数执行(入栈)和函数返回(出栈)。堆栈的三种用途:1.为函数内部声明的局部变量(非静态局部变量)分配内存空间。2.记录函数调用信息,用栈帧表示。除了函数的返回地址外,还有一些进出寄存器的变量。3、暂存区,用于暂存长算术表达式的部分计算结果或alloca()函数分配的栈中的内存。(我不是很清楚)在此基础上,往往会导致一个编程问题,即:栈溢出,这种情况尤其出现在递归调用中,除了申请内存,不释放外,还很可能导致内存泄漏。要避免这种情况,需要注意内存的释放。一般来说,一个new对应一个delete。在linux中,可以使用ulimit-s查看栈的大小。下面是我的linux系统中栈的大小[m@m~]$ulimit-s8192Heap堆是程序运行时动态分配内存的内存段。通常使用malloc和new(底层也是malloc)获取一块内存空间,使用free和delete释放已使用的内存空间。常见问题:由于是程序员手动创建的,所以一定要注意内存释放。问题是类和main函数中动态分配的内存一般都能释放,但有时其他函数中new的内存空间,,却没有删除,导致内存泄漏。多次运行该函数,程序可能会崩溃。#includeint*exp(inti){int*ptr=newint(i);returnptr;}intmain(){inta=1;int*p=exp(a);//删除指针;//错误!由于函数中的ptr已经被回收,所以不能deleteptrdeletep;//释放函数外定义的指针是正确的p=NULL;return0;}下面这句话是一个参考:使用堆时经常会出现两种问题:1)释放或覆盖仍在使用的内存(“内存损坏”);2)不释放不再使用的内存(“内存泄漏”)。当释放次数小于请求次数时,可能已经造成内存泄漏。泄漏的内存往往比忘记释放的数据结构要大,因为分配的内存通常会四舍五入到大于请求量的2次方(比如请求的212B,会四舍五入到256B)。代码段代码段也称为文本域或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。C语言的执行语句一般被编译成机器码存放在代码段中。通常代码段是可共享的,所以频繁执行的程序只需要在内存中有一个副本。代码段通常被归类为只读,以防止其他程序意外修改其指令(写入该段会导致段错误)。一些体系结构还允许代码段是可写的,这允许修改程序。数据段数据段通常用来存放全局变量(包括静态全局变量)和程序中已经初始化过且初始值不为0的静态局部变量。bss段通常存放以下两种情况:1.未初始化的全局变量(Staticglobalvariables)和局部静态变量2.初始化为0的全局变量(staticglobalvariables)和staticlocalvariable内存映射区除了这些,栈和堆之间还有内存映射段(下图)mmap这里引用了三段,内核将硬盘文件的内容直接映射到内存中,任何应用程序都可以通过Linux的mmap()系统调用或者Windows的CreateFileMapping()/MapViewOfFile()来请求这个映射内存。映射是一种方便高效的文件I/O方式,因此被用来加载动态共享库,用户也可以创建匿名内存映射,它没有对应的文件,可以用来存储程序数据。在Linux中,如果你通过malloc()请求一大块内存,C运行时库将创建一个匿名内存映射,而不是使用堆内存。“大块”表示大于阈值MMAP_THRESHOLD,默认为128KB,可以通过mallopt()进行调整。最后附上一段自己写的查看内存布局的代码。Linux下查看内存映射源码在/proc/processnumber/maps:#includevoidget(){inte;诠释f=4;静态整数g;静态整数h=5;std::cout<<"在get()中,地址a为"<<&e<