程序异常分析指南

本文详细探讨了程序异常的两大类别:非法内存访问和资源访问冲突,包括非法指针、多线程共享数据访问冲突、内存访问越界、缓冲区溢出、栈内存溢出、堆内存溢出和死锁等问题。同时,介绍了通过CoreDumped异常分析和Valgrind工具进行内存泄漏和死锁检测的方法,以帮助开发者解决程序异常和优化代码。
摘要由CSDN通过智能技术生成

在Linux上编写运行C语言程序,经常会遇到程序崩溃、卡死等异常的情况。程序崩溃时最常见的就是程序运行终止,报告Segmentation fault (core dumped)错误。而程序卡死一般来源于代码逻辑的缺陷,导致了死循环、死锁等问题。总的来看,常见的程序异常问题一般可以分为非法内存访问资源访问冲突两大类。
在这里插入图片描述
1.非法内存访问(读/写):非法指针、多线程共享数据访问冲突、内存访问越界、缓冲区溢出等。
2.资源访问冲突:栈内存溢出、堆内存溢出、死锁等。

一、非法内存访问

非法内存访问是最常见的程序异样原因,可能开发者看的“表象”不尽相同,但是很多情况下都是由于非法内存访问引起的。

1. 非法指针

非法指针是最典型的非法内存访问案例,空指针、指向非法地址的指针是代码中最常出现的错误。
在这里插入图片描述
示例代码如下:

  long *ptr;
  *ptr = 0;                         // 空指针
  ptr = (long *)0x12345678;
  *ptr = 100;                       // 非法地址访问

无论是访问地址为0的空指针,还是用户态无效的地址,都会导致非法指针访问错误。
实际编程过程中,强制类型转换一不小心就会产生非法指针,因此做强制类型转换时要格外注意,最好事先做好类型检查,以避免该问题。

2. 多线程共享数据访问冲突

在多线程程序中,非法指针的产生可能就没那么容易发现了。
一般情况下,多个线程对共享的数据同时写,或者一写多读时,如果不加锁保证共享数据的同步访问,则会很容易导致数据访问冲突,继而引发非法指针、产生错误数据,甚至影响执行逻辑。

示例代码如下:

 // 全局变量
  long *ptr = (long *)malloc(sizeof(long));

  // 线程1
  if (ptr) {
    *ptr = 100;                               // 潜在的非法地址访问
  }

  // 线程2
  free(ptr);
  ptr = NULL;

上述代码中,全局初始化了指针ptr,线程1会判断该指针不为NULL时进行写100操作,
而线程2会释放ptr指向的内存,并将ptr置为NULL
虽然线程1做了判断处理,但是多线程环境下,则会出现线程2刚调用完free操作,还未来得及将ptr设为NULL 时,
发生线程上下文切换,转而执行线程1的写100操作,从而引发非法地址访问。
在这里插入图片描述
解决并发数据访问冲突的方案是使用锁同步线程。
针对图中的线程同步问题,只需要在线程1和线程2的处理逻辑前,使用读写锁同步即可。
操作系统或者gcc的库函数内也存在很多线程不安全的API,在使用这些API时,一定要仔细阅读相关的API文档,使用线程锁进行同步访问。

3. 内存访问越界

内存访问越界经常出现在对数组处理的过程中。本身C语言并未有对数组边界的检查机制,因此在越界访问数组内存时并不一定会产生运行时错误,但是因为越界访问继而引发的连锁反应就无法避免了。

示例代码如下:

  void out_of_bound() {
    long *ptr;
    long buffer[] = {0};
    ptr = buffer;
    buffer[1] = 0;                // 越界访问导致ptr被覆盖
    ptr[0]++;
  }

示例代码在函数out_of_bound内定义了两个变量:指针ptr和数组buffer
指针ptr指向buffer其实地址,正常情况下使用ptr[0]可以访问访问到buffer的第一个元素。
然而对buffer[1]的越界写操作会直接覆盖ptr的值为0,从而导致ptr为空指针。
在这里插入图片描述
了解该问题的原因需要清楚局部变量在栈内的存储机制。
在函数调用时,会将调用信息、局部变量等保存在进程的栈内。
栈是从高地址到低地址增长的,因此先定义的局部变量的地址一般大于后定义的局部变量地址。
上述代码中,bufferptr的大小都是8Byte,因此buffer[1]实际就是ptr所在的内存。
这样对buffer[1]的写操作会覆盖ptr的值就不足为怪了。总之,对数组访问的时候,做好边界检查是重中之重。
类似的问题也出现在对字符串的操作中,包括gcc提供的字符串库函数也存在该问题,使用时需要尤其注意。

说到边界检查,这里引申出一个话题。在对数组处理时

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值