理解程序内存

2023-05-16

内存对很多人来说感觉是个很熟悉的东西,因为我们在用VC调试程序时,很多时候都会察看内存中变量的值。但是,很多时候我们的思维也就因此局限在有源码的模块,当遇到一些跨模块或是没有源代码的Bug,我们还是无从下手。因此,很有必要我们要对整个程序内存有个比较全局性的认识,这样遇到任何问题,你都能从容面对。

我这里以32位的程序为例,我们知道32位程序总共有4G内存空间,其中低2G是用户地址空间,高2G是内核地址空间,下面我们借助WinDbg工具来分析低2G用户地址空间的内存分布。

因为所有程序的内存分布都大同小异,我这里用WinDbg分析任务管理器进程的内存分布。打开任务管理器,然后用WinDbg Attach到taskmgr.exe进程。
输入 !address -summary 察看内存的使用情况, 结果如下:


从上图可以看到,程序内存根据使用情况大致分为:
Free - 没有被使用的
Image - 加载到内存的模块(dll,exe等)
MappedFile - 内存映射文件
unclassified - 实际上应该是堆(heap)
Stack - 堆栈
TEB - 线程环境块(thread environment block)
PEB - 进程环境块(process environment block)

内存根据使用类型又可以分为:
MEM_IMAGE  - 加载到内存的模块(dll, exe等)
MEM_MAPPED - 内存映射
MEM_PRIVATE - 私有(stack, heap, teb, peb等)

内存根据使用状态又可分为:
MEM_FREE - 空闲
MEM_COMMIT - 已经提交
MEM_RESERVE - 保留

根据页面属性又可分为只读,可读写,可执行,写时拷贝等。

实际上我们可以通过!address命令来查看更详细的内存使用情况:


可以看到上面列出了所有2G用户空间的页面使用情况(截图只是开始的一部分),我们可以根据某个地址来分析该地址属于那块内存区域。当然也可以通过命令来分析某个地址所属的内存区域, 比如输入!address 7c554来分析地址7c554的情况,会显示:

上面告诉我们7c554是某个堆栈(Stack)空间的地址.

对我们程序来说最常接触的内存应该是: Module, Heap, Stack,接下来依次分析.

(1)Module
Module在上面被叫住Image,实际上就是被加载到内存的Exe和DLL文件, 我们可以通过lm命令来查看所有的模块分布情况:

上面可以看到每个模块的内存起始地址,那么各个模块具体内部又是如何分布,它和磁盘上的DLL(exe)文件又是什么关系呢?
实际上内存的中DLL和磁盘上的DLL文件非常相似,系统在加载时只是根据页面大小(一般4K)作了一些对齐,另外有些数据节如果运行时用不到(比如dll的重定位节)就不会被加载.

我们在!address查看内存空间时,可以看到taskmgr.exe模块的内存分布如下:

上面可以看到taskmgr.exe模块在内存中分为4块,第一块是只读的, 实际上是PE文件头;第二块是可执行的,实际上就是代码节(.text);第三块是可读写的,实际上数据节(.Data); 最后一块也是只读的,实际上资源节(.rsrc)。
要详细的了解taskmgr.exe模块的文件头属性,可以通过!dh [module address]来查看, 输入!dh 1000000,查看结果:


上面的运行结果可以验证我们关于taskmgr.exe模块内部分布的猜想.

(2)Heap
Heap实际上就是堆,我们所有new(malloc)出来的内存就是分布在堆里,每个程序会有若干个堆,有些是系统创建的,也有的是C/C++运行库创建的,当然我们自己也可以创建私有堆.我们可以通过!heap命令来查看堆的使用情况.

可以看到taskmgr.exe一共有9个堆。
!heap命令非常强大,通过开启页堆功能,可以很方便的让我们跟踪所有堆内存的分配和使用情况,以后有机会再细说heap相关的.

(3)Stack
Stack即我们通常所说的栈,我们的局部变量就是分配在栈上面。说到栈就要说到线程,我们的代码都是通过线程跑起来的,每个线程包含2块东西,一块是线程内核对象,还有一块就是堆栈,线程运行过程也是堆栈不断压栈和出栈的过程。
我们可以通!address -f:stack 来查看堆栈的分布情况:

从上图我们可以看到taskmgr.exe一共有4个线程, 对应着4个堆栈, 同时也可以看到每个堆栈内存的起始地址。

如果有兴趣,我们也可以看下每个线程的堆栈情况, 输入~* kp

可以看到相应的4个线程堆栈,最后一个线程(debugBreakPoint)看起来有些奇怪,实际上它是调试器为调试而插入的,不是真正的属于taskmgr.exe, 所以任务管理器实际上一共应该有3个线程.

通过上面的介绍,相信大家对程序内存有了比较全局的理解,以后大家分析问题,遇到一个地址,首先要判断这个地址分布在哪里:
如果是Image上,那么是在哪个模块中,这个地址是属于该模块的代码段(.text)还是数据段(.data),如果是代码段,又是属于哪个函数?
如果是Heap上,那么究竟是在哪个堆里面,是我们new出来的吗,是在什么时候new的(new时堆栈状况)?
如果是在Stack上,那么究竟是属于哪个线程的堆栈,当时线程的堆栈是怎么样?

总之,程序在内存中运行,只有你真正理解了内存,你才能真正懂计算机。

转载于:https://www.cnblogs.com/weiym/archive/2012/09/19/2694501.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

理解程序内存 的相关文章

随机推荐

  • 给你两个字符串str1,str2,找出str2在str1中的位置

    如题 题目参考链接 xff1a http blog csdn net hxz qlh article details 14110221 代码来自非原创 1 include lt iostream gt 2 include lt cstdio
  • Win10间歇性卡顿

    Win10间歇性卡顿 1 关闭不必要的服务 xff1a Windows Update Windows Search SuperFetch Background Intelligent Transfer Service 2 卸载电脑管家 xf
  • HTTP协议 (二) 基本认证

    http协议是无状态的 xff0c 浏览器和web服务器之间可以通过cookie来身份识别 桌面应用程序 比如新浪桌面客户端 xff0c skydrive客户端 跟Web服务器之间是如何身份识别呢 xff1f 阅读目录 什么是HTTP基本认
  • 字符串截取函数--C语言(转)

    1 include lt stdio h gt 2 include lt stdlib h gt 3 4 char substring char ch int pos int length 5 6 char pch 61 ch 7 定义一个
  • HTTP, WWW-Authenticate, Authorization 验证授权 | Apache验证 | Python处理

    2019独角兽企业重金招聘Python工程师标准 gt gt gt HTTP验证 有时你访问网页时会碰到这种情况 xff1a 这种方式是基于HTTP协议的一种验证方式 xff0c 这里可以参考HTTP协议的具体解释 xff1a http w
  • 虚拟化(KVM)

    虚拟化介绍 VMware Workstation就是虚拟化 虚拟化简单讲 xff0c 就是把一台物理计算机虚拟成多台逻辑计算机 xff0c 每个逻辑计算机里面可以运行不同的操作系统 xff0c 相互不受影响 xff0c 这样就可以充分利用硬
  • 头文件中的(全局)变量只能放声明,不能定义

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 头文件中的变量只能声明 xff0c 不能定义 xff0c 否则其他多个 c文件包含该头文件 xff0c 出现重复定义 xff0c 导致链接出错 ifndef define
  • 程序启动时的堆栈

    程序启动时 xff0c Linux把4种类型的信息存放到程序堆栈中 xff1a xff08 1 xff09 命令行参数 xff08 包括程序名称 xff09 的数目 xff08 2 xff09 从shell提示符执行的程序的名称 xff08
  • suse linux双网卡双网关配置

    公司有台SUSE LINUX服务器需要用到2个网络 xff0c 一个网段是192的 xff0c 用来上OP管理平台系统 一个是B网络 xff0c 网段是202的 xff0c 用来上外网 我们都知道一台电脑拥有两个网关是不可能的 xff0c
  • 统计学中抽样比例一般占百分之多少

    要具体问题具体分析 一般和要求的误差限 置信区间及总体方差有关系 比如最基本的简单随机抽样 其样本量确定公式就是1 n 61 1 N 43 d 2 u 2 S 2 样本量和误差限成反比 和置信区间及总体方差成正比 请问一堆url怎么算方差
  • emqtt 试用(二)验证 emq 和 mosquito 的共享订阅

    本地订阅 Local Subscription 本地订阅 Local Subscription 只在本节点创建订阅与路由表 xff0c 不会在集群节点间广播全局路由 xff0c 非常适合物联网数据采集应用 使用方式 订阅者在主题 Topic
  • spring security实现动态配置url权限的两种方法

    缘起 标准的RABC 权限需要支持动态配置 xff0c spring security默认是在代码里约定好权限 xff0c 真实的业务场景通常需要可以支持动态配置角色访问权限 xff0c 即在运行时去配置url对应的访问角色 基于sprin
  • 干货!2018年你值得一看的网页设计作品集赏析

    网页设计作品集 61 门面 43 能力 网页设计作品集对网页设计师而言 xff0c 既是网页门面 xff0c 也是个人专业素养的体现 那么在作品集设计上万不能掉以轻心 无论是制作一份简约大方还是极具表现力的精良作品集 xff0c 设计师们都
  • ElasticSearch在原索引基础上添加字段和修改字段

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 业务场景 xff1a 现在的系统设计不满足业务需求时就需要升级服务 xff0c 为了满足业务需求有时需要对ES中 字段进行添加或修改操作 xff0c 接下来我们查看为 ma
  • echarts中关于字体设置的配置

    1 legend字体大小的配置 2 x轴和y轴上的刻度值的字体大小的配置 3 visualMap的字体大小的配置 4 饼状图上的文字大小的配置 其对应的是 转载于 https blog 51cto com 11871779 2401302
  • 堡垒机 jumpserver

    堡垒机介绍 在一个特定网络环境下 xff0c 为了保障网络和数据不受外界入侵和破坏 xff0c 而运用各种技术手段实时收集和监控网络环境中每一个组成部分的系统状态 安全事件 网络活动 xff0c 以便集中报警 及时处理及审计定责 我们又把堡
  • vim替换命令

    在VIM中进行文本替换 xff1a 1 替换当前行中的内容 xff1a s from to xff08 s即substitude xff09 s from to xff1a 将当前行中的第一个from xff0c 替换成to 如果当前行含有
  • xxxxxxxxxxxxxxxxxxxxxxxxxxxx

    Get Authorization code Request https accounts google com o oauth2 v2 auth redirect uri 61 https 3A 2F 2Fdevelopers googl
  • linux 系统密码忘记后的 5 种方法【转】

    如果你既没做系统启动软盘 xff0c 同时多系统的引导LILO 和GRUB 又被删除 xff0c 那么只能使用Linux 系统安装盘来恢复root的密码 xff0c 步骤如下 一 lilo引导在出现 lilo 提示时键入 linux sin
  • 理解程序内存

    内存对很多人来说感觉是个很熟悉的东西 xff0c 因为我们在用VC调试程序时 xff0c 很多时候都会察看内存中变量的值 但是 xff0c 很多时候我们的思维也就因此局限在有源码的模块 xff0c 当遇到一些跨模块或是没有源代码的Bug x