内存是C/C++程序员的好帮手,我们通常说C/C++程序性能更高其原因之一就在于可以自己来管理内存,然而计算机科学中没有任何一项技术可以包治百病,内存问题也给C/C++程序员带来无尽的烦恼。

野指针、数组越界、错误的内存分配或者释放、多线程读写导致内存被破坏等等,这些都会导致某段内存中的数据被”无意“的破坏掉,这类bug通常很难定位,因为当程序开始表现异常时通常已经距离真正出问题的地方很远了,常用的程序调试方法往往很难排查此类问题。

既然这类问题通常是由于内存的读写造成,那么如果要是某一段内存被修改或者读取时我们能观察到此事件就好了,幸运的是这类技术已经实现了。

一段示例

在GDB中你可以通过添加watchpoint来观察一段内存,这段内存被修改时程序将会停止,此时我们就能知道到底是哪行代码对该内存进行了修改,这功能是不是很强大。

接下来我们用示例来讲解一下,有这样一段代码:

#include <iostream>
#include <thread>
using namespace std;// 线程修改变量值
void memory_write(int* value) {*value = 1;
}int main()
{int a = 10;// 获取局部变量a的地址int* c = &a;for (int i = 0; i < 100; i++) {a += i;}cout << a << endl;// 将变量a的地址传递到线程thread t(memory_write, c);t.join();return 0;
}

这段代码非常简单,创建局部变量a,然后获取变量a的地址并赋值给指针c,此后对变量a进行累加和,然后输出a的值,此时a的值为4960。

假设此后你发现变量a的值竟然变为了1,然而由于代码非常复杂你并不知道到底是哪段代码对变量a进行修改,在上述代码中我们利用线程a来模拟这个场景,线程获取变量a的地址后对其进行了修改,将其变为了1,接下来我们利用调试工具gdb来定位到底是谁修改了变量a。

资料直通车:Linux内核源码技术学习路线+视频教程内核源码

学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈

开始捕捉“肇事者”

对上述代码进行编译,接下来利用gdb进行调试,假设源文件的名称是a.cc,编译后的可执行程序名字为a:

$ gdb a.out
(gdb) b a.cc:20
Breakpoint 1 at 0x400f23: file a.cc, line 20.
(gdb) r
Starting program: /bin/a
Breakpoint 1, main () at a.cc:20
20          cout << a << endl;

上述调试命令(b a.cc:20)表示我们在代码的第20行加断点,当程序运行到这里后暂停,调试命令r表示开始运行程序,可以看到运行到第20行后暂停,此时我们查看一下变量a的地址:

(gdb) p &a
$1 = (int *) 0x7fffffffe508

可以看到,变量a位于内存地址0x7fffffffe508,接下来重点来了,我们该怎样告诉gdb让它帮我们时刻监测0x7fffffffe508这个内存地址中的值有没有被修改呢?很简单:

(gdb) watch *(int*)0x7fffffffe508
Hardware watchpoint 2: *(int*)0x7fffffffe508

我们利用watch命令,让gdb帮我们时刻监测一段从0x7fffffffe508开始大小为4字节的内存区域(假设int占据4字节),这就是watch *(int*)0x7fffffffe508这行指令的含义:

除此之外上面gdb的输出中还有一段值得注意:

Hardware watchpoint 2: *(int*)0x7fffffffe508

注意看,什么是Hardware watchpoint呢?先卖个关子,我们稍后聊,接下来我们运行gdb中的c命令,意思是continue,让程序继续运行:

(gdb) c
Continuing.
4960

此时第20行执行完毕并打印出了变量a的值4960,我们接着往下看:

[New Thread 0x7ffff6f5c700 (LWP 531823)]
[Switching to Thread 0x7ffff6f5c700 (LWP 531823)]
Hardware watchpoint 2: *(int*)0x7fffffffe508Old value = 4960
New value = 1
memory_write (value=0x7fffffffe508) at a.cc:8
8       }
(gdb)

哈哈,gdb成功的捕捉到了是哪一行代码修改了0x7fffffffe508这块内存,而且详细的告诉我们所有信息,可以看到gdb打印出了这块内存之前保存的数据是数字4960,修改后的值为1,并且是在a.cc:8这里被修改的,而这里正是我们创建的线程对变量a进行修改的地方,gdb成功的捕捉到了”肇事者“,原来是这个线程”无意“修改了变量a的值。

是不是很神奇,那么这一切都是怎样实现的呢?

watchpoint是怎样实现的?

原来这一切都是CPU的功劳。

现代处理器中具有特殊的debug寄存器,x86处理器中是DR0到DR7寄存器,利用这些寄存器硬件可以持续检测处理器发出的用于读写内存的地址,更强大的是,不但硬件watchpoint可以检查内存地址,而且还是可以监测到底是在读内存还是在写内存。

利用gdb中的rwatch命令你可以来监测是否有代码读取了某段内存;利用gdb中的awatch命令你可以来检查是否有代码修改了某段内存;利用gdb中的watch命令你可以检查对某段内存是否有读或者写这两种情况。

一旦硬件监测到相应事件,就会暂停程序的运行并把控制权交给debugger,也就是这里的gdb,此时我们就可以对程序的状态进行详细的查看了,这种硬件本身支持的调试能力就是刚才提到的Hardware watchpoint

有hardware watchpoint就会有software watchpoint,当硬件不支持hardware watchpoint时gdb会自动切换到software watchpoint,此时你的程序每被执行一条机器指令gdb就会查看相应的事件是否发生,因此software watchpoint要远比hardware watchpoint慢,你可以利用gdb中的”set can-use-hw-watchpoints“命令来控制gdb该使用哪类watchpoint。

值得注意的是,在多线程程序中software watchpoint作用有限,因为如果被检测的一段内存被其它线程修改(就像本文中的示例)那么gdb可能捕捉不到该事件。

一篇知晓-内存竟被”无意“破坏,真相究竟如何?相关推荐

  1. 内存竟被”无意“破坏,真相究竟如何?

  2. FreeRTOS高级篇7---FreeRTOS内存管理分析

    内存管理对应用程序和操作系统来说都非常重要.现在很多的程序漏洞和运行崩溃都和内存分配使用错误有关.         FreeRTOS操作系统将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理 ...

  3. Android性能:内存篇之内存回收

    Android性能:内存篇之内存回收 在学会内存性能优化之前我们得先了解内存如何回收,在<Android性能:内存篇之虚拟机概论>我们已经了解了虚拟机的概念及JVM结构体系与内存空间,在& ...

  4. 服务器内存超频性能,测试篇:内存超频性能测试

    测试篇:内存超频性能测试 我们把内存分为"DDR2 667".DDR2 800及以上"进行比较,通过32M SUPER PI作为一些特别为超频玩家而推出的内存将会作加电压 ...

  5. 计算机评分主硬盘分数低,鲁大师跑分详解-内存篇:内存跑分为什么比别人低?分数到底差在哪?...

    原标题:鲁大师跑分详解-内存篇:内存跑分为什么比别人低?分数到底差在哪? 今天鲁sir不说别人家的手机了,说说自己家的鲁大师,回归老本行,接下来呢给大家详细解释一下鲁大师跑分项目. 本期是第一期,所以 ...

  6. Android中apk加固完善篇之内存加载dex方案实现原理(不落地方式加载)

    一.前言 时隔半年,困扰的问题始终是需要解决的,之前也算是没时间弄,今天因为有人在此提起这个问题,那么就不能不解决了,这里写一篇文章记录一下吧.那么是什么问题呢? 就是关于之前的一个话题:Androi ...

  7. 软件调试系列:软件崩溃篇之内存异常崩溃

    导致崩溃的情况很多,同样崩溃的表现也是千差万别,既然如此,那么还是让我们先来看一下这个崩溃是如何用Windbg分析的吧. 某年某月某日,测试人员报告说,Sample.exe软件崩溃了,两名开发人员小崔 ...

  8. 看完这篇JVM内存管理机制,面试再也不慌了!

    /   今日科技快讯   / 近日,美国新冠肺炎确诊病例已破300万例,众多美企深受疫情打击.然而,特朗普政府当前正全力推进一项针对华为.中兴等中企产品的采购禁令,又让一众美企措手不及.7月10日,代 ...

  9. [JAVA]第二篇(内存管理,HashMap内存泄漏解决办法)

    网上看到一个关于内存泄漏处理的例子,原网址:http://www.jb51.net/article/49428.htm,下面笔者将具体分析下这篇文章中的代码,并从中学习JAVA的内存管理. (Begi ...

最新文章

  1. [转]优秀编程的“艺术”
  2. 为什么建议大家使用Linux开发?
  3. 交换机的4种网络结构方式你知道是什么吗?
  4. mysql jar jdk1.6_Windows下JDK1.6+MySQL+MyEclipse开发环境的配置
  5. 【论文阅读】医疗影像图像增强
  6. 并发 (一)——基本概念
  7. C++解压KRC文件
  8. Powershell的字符串
  9. 甲子光年推出中国低代码行业分析报告:本地私有化部署占比超过一半
  10. 群体创新技术/群体决策的几种类型
  11. 【数据挖掘】《数据分析与数据挖掘》--天津大学公开课
  12. 打造全新的网站群管理系统
  13. redis设置密码并修改查看的几种方式
  14. 建站选择免费虚拟主机的六大误区
  15. Scientific Linux 6(x86_64) 之旅
  16. centos7-登录显示名称及登录欢迎界面设置
  17. JAVA 网页转图片
  18. MATLAB 2017a \b、2016a\b等高版本,打开函数帮助文档需要登录MATHworks账户且需要绑定产品
  19. 腾讯电脑管家,vs安装文件报成木马,还能信吗?
  20. Android RxJava操作符的学习---总结

热门文章

  1. html调起苹果手机摄像头_html5摄像头 如何调用手机摄像头
  2. 一加7充电_一加7T Pro发布日期已经确认,2K真全面屏+无线充电,价格感人
  3. 面试官画像(十年沉浮 | 番外篇)
  4. java web程序示例_示例Web应用程序提示列表
  5. OSEA视频认证,技能强国,筑梦未来技能人才培养
  6. 北京理工大学计算机学院课程表,北京理工大学工业设计课程表.doc
  7. 13亿热钱苦候半年 优酷网获视频牌照
  8. 氛围灯INMP441+WS2812灯带--未完
  9. html5div居中属性,html怎样让div居中
  10. java schtasks 不生效,关于计划的任务:在Windows的schtasks命令中指定“启动”目录...