中断和异常,C C++语言异常,实时操作系统对中断和异常的处理_c++中断(1)

9)开中断、中断返回。中断服务程序的最后一条指令通常是一条中断返回指令,使其返回到原程序的断点处,以便继续执行原程序。

其中,13步是在CPU进入中断周期后,由硬件自动(中断隐指令)完成的;49步由中断服务程序完成。恢复现场是指在中断返回前,必须将寄存器的内容恢复到中断处理前的状态,这部分工作由中断服务程序完成。中断返回由中断服务程序的最后一条中断返回指令完成。

3. 常见中断和异常

常见异常:

  • 算数溢出
  • 除零
  • 取数奇偶校验错
  • trap指令(system call)
  • 硬件改变cpu执行流程
  • 地址越界
  • 页错误/故障
  • 保护性异常(写仅读内存)
  • 断点指令

常见中断:

  • I/O中断
  • 时钟中断
  • 硬件故障

二、C/C++软件异常的常见原因分析与总结

前面讲的是宽泛的在操作系统中的中断和异常。下面我们来聊聊编程语言方面的异常。

已剪辑自: https://blog.csdn.net/chenlycly/article/details/124996473

在C++软件开发和维护的过程中,会遇到各式各样的软件异常,这些问题的分析和排查可能会消耗大量的时间和精力,会给软件研发流程的顺利推进带来不利的影响。本文根据近几年排查C++软件异常的实践经历与实战经验,详细地总结出引发C++软件异常的常见原因,给大家提供一些借鉴和参考,以帮助大家快速地定位问题。

1、概述

cfd5d99baf65441da2704510dd59d99f.png

​ 作为一名经验丰富的C++软件研发人员,除了要有良好的代码编写与设计能力,还要有很强的代码调试能力和异常分析能力。而这些能力的培养不是一撮而就的,是通过大量的项目实践锻炼出来的。本文要讲述的内容不仅适用于刚入门的C++开发新人,也适用于有多年工作经验的C++开发人员。

​ 本文所讲的C++软件异常,是广义上的C++软件运行异常,包括软件运行时逻辑上的异常以及引发软件卡死或崩溃的异常。引发C++软件异常的常见原因有变量未初始化、内存越界、内存访问违例、Stack Overflow线程栈溢出、空指针与野指针、死循环、死锁、内存泄露、GDI对象泄露、函数调用约定不一致导致的栈不平衡等,下面将对这些原因及场景进行详细地阐述。

希望大家在了解本文的内容之后,既能在编写代码时提前感知潜在的问题,也能在出问题之后多一些排查问题的思路与手段。

​ 此外,本文主要以PC端的Windows平台下的C++开发为例进行讲解,不同平台及不同语言在多个方面是相通的,也具有一定的参考价值。

2、引发软件异常的常见原因

​ 下面就来详细地讲述一下引起软件异常的常见原因及相关场景。

2.1、变量未初始化

​ 这可能是许多人容易忽略的问题。如果在定义变量时没有对变量进行初始化,在Debug下某些编译器会自动对变量自动进行初始化,但在Release下编译器不会对变量初始化,此时变量的值则是分配到内存时内存中的随机值。

​ 对于微软C++编译器,在Debug下,未初始化的栈内存会被编译器初始化为0xCCCCCCCC,未初始化的堆内存会被编译器初始化为0xDDDDDDDD,这些特殊的异常值说明如下:

7ca30bce96f54e0fa0dab7dd5c5021a5.png

​ 在调试代码时,如果遇到0xCCCCCCCC、0xDDDDDDDD异常值时,可能是变量未初始化,如果访问这些变量,则会报如下的内存访问违例错误:

fdbf0c5393d740babe8abe97edfb173c.png

如果遇到0xFEEEFEEE异常值时,可能是堆内存被释放了,如果继续将其当做有效的内存地址去访问内存,则会报如下的内存访问违例错误:

6d73296948d2402c8a499df48ca163b7.png

如果代码中使用了没有初始化的变量,可能会导致Debug和Release下运行行为的不一致(因为Debug下编译器会自动初始化,Release下未初始化的变量的值是随机值),也可能会导致不同版本操作系统上运行行为的不一致(不同版本操作系统的内存管理机制是有差异的,比如win7和win10系统)。

​ 具体地说,代码中使用了未初始化的变量,可能会导致代码逻辑出异常,可能会导致代码产生崩溃。比如在if条件语句中使用了未初始化的变量,会直接影响程序的走向。比如访问了一个没有初始化的C++对象指针,以该指针中存放的值作为一个C++地址去访问该对象的数据成员,可能会导致内存访问违例,引发程序崩溃。再比如使用了一个未初始化的函数指针(比如存放上层设置下来的回调函数地址的函数指针),在通过该函数指针去调用函数时会是致命的,会导致程序“跑飞”了,导致程序出现胡乱的崩溃。为什么会导致程序跑飞了呢?是因为函数指针没有初始化就去访问,函数指针的值就是个随机值,根本不是一个有效的函数地址,把这个无效值作为函数地址去call,会导致不可预料的结果,可能导致程序出现胡乱的崩溃。

​ 可能有人会说,只要控制好现有的代码,就能保证不会访问未初始化的变量,不一定非要一上来就将变量初始化。即便是这样,也是不可取的,因为代码后面可能会交由其他人维护,每个人编写代码的水平是有差异的,你这里可以保证没问题,别人那边可能就会出问题。

2.2、死循环

736cdbb8267a4a8cbb2c5a4668a6d3a9.png

​ 死循环一般会引发CPU高占用率,一般不会导致软件崩溃。一旦发现进程占用的CPU过高,可能就是死循环触发的,当然也有可能是新开线程的线程函数中没有添加sleep导致的(线程中不能一直在处理事务,要适当的“休息休息”)。

​ 死循环一般是for循环或while循环的循环控制条件出了问题可能是循环控制条件写错了(比如编写代码时的手误,比如将小于号写成了等于号,导致循环条件一直成立,循环一直退不出来),也可能是循环控制条件中变量出现了异常大的值(这个值可能是服务器返回的异常值,应该要添加值的合理性判断)。这两类问题我们在联调时都遇到过。

​ 另外,还有一种情况是消息上触发的函数调用上的死循环。比如调用一个底层的接口,收到底层回应消息后,又调用了该底层接口,又收到底层的这个消息,循环往复地执行同样的函数调用,导致了死循环的发生。这样的例子,我们遇到过不止一次了。

​ 再比如在窗口消息WM_MESSAGE1的处理函数A中调用了B函数,B函数又调用了C函数,C函数中又调用SendMessage发出了WM_MESSAGE1,由于SendMessage是直接把消息发给窗口处理过程,等消息被处理完后才会返回,相当于代码是同步执行的,所以又进入了A函数中,这样就形成了消息触发的函数调用的死循环,我们在项目中曾经遇到过一次。

​ **死循环会导致所在线程的卡顿或堵塞,**如果发生在UI线程中,UI界面会出现点击没反应或者反应很慢的情况。对于Windows程序,可以使用Windbg或者Process Explorer去定位死循环所在线程及函数。

Windbg和Process Explorer是排查死循环和CPU高占用问题的利器,都可以查看到进程中所有线程的CPU占用情况,可以进一步查看CPU占用高的线程的函数调用堆栈。相比较而言,Process Explorer要更易操作一点,但就调试功能而言,还是Windbg要强大很多。哪些场景下使用哪种工具,视个人喜好及问题的具体情况而定吧。

​ 另外,这两个工具是需要pdb符号库文件的,因为要显示详细的函数调用堆栈信息(显示具体的函数名)是需要pdb符号库的(符号库中有函数及变量的详细信息)。对于windbg,需要将pdb文件的路径设置到windbg中。对于Process Explorer,只需要将pdb放置在目标exe的同级目录中,Process Exploer回到exe程序所在的目录 中去自动搜索并加载pdb文件的。

​ 关于排查CPU占用高的案例,可以参见文章:

2.3、内存越界

​ **内存越界是指操作某个变量内存时,超过了该变量的内存范围,越界到该变量内存后面的内存上了。**内存越界会篡改越界部分的内存中的内容,但内存越界不一定会导致崩溃,越界的内存区域可能是其他变量的内存区域,所以可能会篡改了其他变量的值。其他变量的值被篡改了,可能会导致软件业务出现问题,可能导致代码的运行控制逻辑出异常。

内存越界包含栈内存越界、堆内存越界以及全局内存越界,这些类型的内存越界,我们之前都遇到过。

1)函数的局部变量是栈上分配内存的,对栈内存的越界称之为栈内存越界;

2)通过new和malloc等动态申请内存的,都是在堆上分配的,对堆内存的越界称之为堆内存越界。

3)全局变量和static静态变量都在全局内存上分配内存的,对全局内存的越界称之为全局内存越界。

对于程序内存分区,可参见文章:

实例详解C++程序的五大内存分区imghttps://blog.csdn.net/chenlycly/article/details/120958761 对于栈内存越界,有可能越界到当前函数的其他局部变量上,另外在函数调用时,主调函数的返回地址、主调函数的ebp栈基址、用于esp校验的cookie值等都存在栈上的,有可能会越界到这些内存区域上,如果内存越界将这些内存破坏掉了,则会引起比较致命的错误。比如篡改了主调函数的返回地址,那么等被调函数返回时ÿ

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值