​ 对于C/C++程序员来说,踩内存问题是最难分析定位的,往往问题的源头都比较隐蔽,让人很难排查出问题的根源所在。


原因

  • 内存越界

    1. memset/memcpy/memmove 等内存操作函数操作越界,使用这些函数时尽可能用 memset_s/memcpy_s/memmove_s 等安全函数替代。
    2. 内存越界的问题有个显著的特点,被踩的内存是连续的,遇到这种情况首先怀疑在被踩内存前面申请的内存的操作是否越界。
  • 数组越界/操作字符串越界

    数组越界访问往往是最常见的踩内存原因,对于数组访问一定要先判断下标是否越界

  • 野指针

    1. 对于全局的变量在释放内存后一定要置为NULL,防止出现野指针
    2. 警惕浅拷贝潜在的野指针风险。浅拷贝只是拷贝了它在栈中存储的指针,它们指向的都是同一个堆内存地址,所以浅拷贝在某些情况会造成改变数据后导致别的另一份数据也同步被改变的情况;而深拷贝是直接将堆内存中存储的数据直接复制一份,不会有浅拷贝互相影响的问题。
    3. 某些时候查看 coredump 现场发现程序访问的地址非常小,首先怀疑空指针。因为结构体的基地址是0,程序在访问结构体成员时会进行若干字节的偏移,所以最终挂死的现场的地址非常小。
  • 多线程场景下,全局资源未作加锁保护

    这种情况可能更加复杂,需要对代码进行深度检视


常用定位手段

  • 使用系统提供的函数修改内存为只读属性,发生踩内存时程序会直接崩溃,查看相关 coredump 文件进行分析定位

    1. 用户态进程:mprotect

      #include <unistd.h>
      #include <sys/mmap.h>
      int mprotect(const void *start, size_t len, int prot);
      

      函数使用方法参考:https://www.cnblogs.com/ims-/p/13222243.html

    2. 内核:set_mem_rw

      #include <linux/kallsyms.h>
      int set_mem_rw(unsigned long ,int)
      set_mem_rw = (void *)kallsyms_lookup_name("set_memory_rw"); // 这个函数内核并未导出,不能直接调用
      
  • valgrind 工具分析

    valgrind是一个强大的内存管理工具,常用来检测内存泄漏和内存的非法使用

    具体使用方法参考:https://blog.csdn.net/weixin_29666341/article/details/116637397

  • GCC 栈保护机制 stack-protector

    程序执行栈内存被踩,程序崩溃的位置是不确定的,为了使崩溃的地点更加接近问题点,可以在编译时加入相关编译选项

    具体使用方法参考:https://www.cnblogs.com/arnoldlu/p/11630979.html