网站建设 推广,个人作品网站,wordpress轮播图,做胎压的网站学习交流加 个人qq#xff1a; 1126137994个人微信#xff1a; liu1126137994学习交流资源分享qq群#xff1a; 962535112 在我之前学习底层的知识的时候#xff0c;也写过相关的内容。可以对比的学习#xff1a;【软件开发底层知识修炼】二十 深入理解可执行程序的结构 1126137994个人微信 liu1126137994学习交流资源分享qq群 962535112 在我之前学习底层的知识的时候也写过相关的内容。可以对比的学习【软件开发底层知识修炼】二十 深入理解可执行程序的结构【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁 学习本文的前提是了解进程的内存布局空间。可以看上面两篇博客进行巩固 文章目录1 程序中的栈内存结构1.1 函数的调用过程对应的栈的变化1.11 函数调用栈上的数据2 程序中应该称为进程中才对的堆结构3 程序中的静态存储区其实就是数据区4 总结 1 程序中的栈内存结构
栈在程序中用于维护函数调用的上下文函数中的参数和局部非静态变量存储在栈上esp指针始终指向栈顶ebp是函数栈帧用于定位的使用ebp可以查找到函数的参数返回地址等信息。后面的函数调用分析会认识到这一点栈在整个进程内存空间中是从上往下扩展也就是从高地址往低地址扩展。如下图是一个栈 push 操作相当于往栈中填数据esp指针会向下走。pop操作相当于将栈顶数据弹出esp指针会网上走。 那么栈对于程序而言到底有什么用呢
栈用于保存一个函数调用的时候所需要维护的信息包括函数的参数、返回地址、局部变量、上下文信息等。它们在栈中的位置大致如下图所示 1.1 函数的调用过程对应的栈的变化
每次函数调用都对应一个栈上的活动记录被调用函数的活动记录位于调用函数的下面。比如有如下的几个函数调用 那么在某一个时刻栈中的内容大致是这样的 注意上面的栈中的内容并没有具体只是说明各个函数的活动记录信息在哪个文职具体里面的内容没有列出。看下面的分析
从程序开始运行时分析
从main函数开始运行
当main开始运行的时候main函数的栈的信息是下面这样的 可以看到首先入栈的是函数参数然后是函数的返回地址old_ebp代表调用main函数的ebp的位置。这个暂且不管可以看到ebp向上偏移4字节在X86 32位系统中栈是以4字节为单位进行存储数据所以一次偏移就是4字节就能找到返回地址这个返回地址是调用main函数的那个函数之前执行指令的地址。ebp向下偏移4字节就是old_ebp。所以说ebp是函数栈帧用于定位查找其他参数esp始终指向栈顶
当main函数调用f()函数
当main函数执行到调用f() 函数的时候main函数的活动记录寄存器返回地址等需要保存入栈f() 函数的参数信息需要入栈。如下图所示 可以看到
f函数的参数先入栈然后返回地址入栈main函数调用f()函数那里的地址然后main函数的ebp的地址入栈。用于定位上一个函数的ebp然后才是函数中的局部非静态变量信息入栈。这个参数的入栈顺序可以参考本文开头给出的两篇文章中的内容
f函数调用g函数就是一样的过程这里就不再赘述。下面直接上当f执行完返回的过程是怎样的
当从f()调用中返回到main函数的栈的变化 上述f返回后当前栈就只有白色空白部分下面的深颜色橘黄色分不清并不是当前栈的内容了 可以看出
通过f函数栈帧中的old_ebp找到main函数的ebp然后将当前ebp寄存器指向它。通过f栈帧中的返回地址找到main函数之前被中断的地址处main函数继续执行。关于这里非常详细的文章请参考【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁
1.11 函数调用栈上的数据
函数调用栈上的数据在函数返回时将被释放不再有效。所以对于以下代码是错误的代码 2 程序中应该称为进程中才对的堆结构
堆空间是进程的内存空间中一块预留的内存空间供程序在运行的时候动态分配。使用malloc与free进行堆中的内存的动态申请(具体的使用可以参考这篇文章【C语言进阶深度学习记录】三十三 C语言中动态内存分配
对于堆空间本文只简单的讲述系统对堆空间的管理方式也就是使用malloc时系统是如何申请内存的以及系统对内存的管理方式当然本文也是简单描述具体可以参考CSAPP书籍中相关章节
操作系统对堆的管理方式主要有
空闲链表法、位图法、对象池法
下面主要简要说明空闲链表发的原理
空闲链表就是操作系统将整个可用的堆内存空间分为一块一块的对应的相同数量的指针指向各个内存块然后内存块的末尾又是一个指针指向下一个内存块的头。
其实简单来说就是将多个内存块串联成一个链表形式。
如下图所示 当使用malloc函数进行内存分配的时候系统会遍历链表找到能够满足申请大小的且没有被别人申请的空闲的链表的一个节点对应的内存块。比如上图申请一个4字节的内存最终遍历链表找到了一个大小为5字节的内存块然后系统就为我们在该内存块上申请4字节的内存空间供指针p使用。
简单来说空闲链表法就是上述的大致过程。
想要深入了解操作系统对堆空间的管理可以阅读书籍《深入理解计算机系统》csapp。本文不再重复赘述。
3 程序中的静态存储区其实就是数据区 我们一般说进程但这里说程序。有些术语描述真的是很模棱两可但是只要自己明白就行 在进程的地址空间栈堆是程序运行时的效果。我们也知道这两个内存对应的数据到底是什么。
那么对于下面程序中的变量g_init_v,g_uninit_v,s_v1,s_v2以及字符串字面量它们是存储在哪里呢 我们已经看到了上图中它们在可执行文件中都是存储在.data区.bss区。这些区域。 当程序运行起来之后它们还是会在进程的内存空间的.data与.bss段。与可执行文件的data与bss名字一样如下图 我们称之为静态存储区。为什么叫静态并不是因为static而是因为它们的值虽然有可能被改变但是却一直在那个区域。不像栈区域的值最后会被销毁堆上的值也需要free。
现在终于明白静态存储区实际就是.data与.bss区域了。
下面总结一下静态存储区的几点重要知识
静态存储区是随着程序的运行而分配空间静态存储区的生命周期直到程序运行而结束在程序的编译期间静态存储区的大小就已经确定静态存储区主要用于保存全局变量和静态局部变量以及类似于字符串字面量样式的字面量
4 总结
栈堆与静态存储区是进程地址空间的基本数据区域。
一定要注意区分可执行文件中的内容与程序运行起来后的进程地址空间中的内容的差别。本文所描述的就是进程地址空间中内容。可执行文件的内容可参考书籍《程序员的自我修养》