【linux】Valgrind工具集详解(九):Memcheck检查的内容和方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010168781/article/details/83781852

一、值的有效性

1、什么是值的有效性?
英文原文是Valid-value (V) bits,直译过来就是有效值(V)位。
我将它理解为值的有效性,就是判断在内存或CPU的物理地址中存储的数据是否有效,比如在内存中变量(int i)代表的物理位置(不是地址),没有初始化,就去使用它,是否合法,参见下面的判断。

2、当仅仅是复制未初始化的值,并且不使用它时,Memcheck不会报告错误,认为是有效的。
例子代码如下:

int i, j;
int a[10], b[10];
for ( i = 0; i < 10; i++ ) {
  j = a[i];
  b[i] = j;
}

上述代码中数组a没有赋值,将数组a复制给数组b,虽然这段代码没有意思,但是Memcheck不会报错。

3、Memcheck会检查以下三种情况
当使用值生成内存地址;
需要进行控制流决策时;
检测到系统调用时。
例子代码如下:

for ( i = 0; i < 10; i++ ) {
  j += a[i];
}
if ( j == 77 ) 
  printf("hello world!\n");

由于“j”没有初始化,并且被用到if(控制流决策),所以此处Memcheck会报告错误。

二、地址的有效性

英文原文是Valid-address (A) bits:有效地址(A)位。
内存的一个物理位置中的数据是否有效,我们称为值的有效性;是否可以合法地读取或写入该位置(即,是否可以访问该位置),来判断地址的有效性。
哪些地址有效:
1、程序启动时,所有全局数据区域都标记为可访问(地址有效)。
2、当程序执行 malloc、new,分配的区域标记为可访问(地址有效),没有分配的依然时无效的;在释放该区域后,该区域标记位不可访问(地址无效)。
3、栈的数据,即局部变量地址有效。实现方法是,根据栈指针寄存器(SP)的移动来触发标记哪些是地址有效、哪些已经无效了。规则是从 SP堆栈的底部到堆栈的区域被标记为可访问,并且下面的区域SP是不可访问的。

三、Memcheck的检查机制可归纳如下:

1、存储器中的每个字节有两个属性:该字节中的值是否有效和该字节是否可以访问;
2、读取或写入存储器时,会检查地址是否有效,如果是无效的地址,则Memcheck会发出无效读取或无效写入错误;
3、当存储器读入CPU寄存器或从寄存器写入存储器时不会检查值的有效性;
4、当CPU寄存器中的值用于生成存储器地址或确定条件分支的结果时,将检查这些值的有效性,如果未定义任何值,则发出错误;
5、一旦检查这些值的有效性后,就将它们设置为检查过的,即标记为值有效,以后再检查就认为是有效的,这避免了重复错误。
6、从内存加载值时,Memcheck会检查该地址是否有效,并在需要时发出非法地址警告。在这种情况下,尽管地址无效,也会将该值标记为有效的,目的是减少呈现给用户的混乱信息量。这样避免了既地址无效又值无效的现象,准确定位错误原因。
7、对于来自部分有效且部分无效的地址的多字节加载,存在模糊的边界情况。有关详细信息,请参阅选项–partial-loads-ok
8、Memcheck会记录分析如下函数:malloc、calloc、realloc、valloc、memalign、free、new、 new[]、delete和 delete[]。
8.1、malloc、new、new[]:分配的内存被标记为可寻址的但不具有有效的值。这意味着初始化后才能使用它们。
8.2、calloc:分配的内存标记为可寻址和有效,因为calloc将区域清除为零。
8.3、realloc:如果新分配的内存大于旧的,则多出的部分标记为地址有效但值无效,如同 malloc。如果新分配的内存小于旧的,则失去的部分标记为不可寻址(地址无效)。
8.4、free、delete、delete[]:传递给这些函数的指针(指向的地址)必须是之前malloc、new、new[]等返回的,否则,Memcheck会报错。如果指针确实有效,则Memcheck将其指向的整个区域标记为不可寻址(释放后地址无效了),并将该块放置在freed-blocks-queue中,目的是尽可能延迟重新分配这个区块。如果在释放后,再去访问它就会引发无效地址错误。

猜你喜欢

转载自blog.csdn.net/u010168781/article/details/83781852