MIPS架构番外篇1-一条小小的除法指令引起的翻车事故

简介: MIPS架构番外篇1-一条小小的除法指令引起的翻车事故

1 事故背景


  • 人物:小T(研发中心-操作系统开发工程师);小S(产品开发部-软件工程师)
  • 背景:公司正在联合开发基于MIPS架构的产品。研发中心负责操作系统平台开发,产品开发部负责业务逻辑开发。目前操作系统已经进入试用阶段。


2 事故现场


操作系统上线后,一直比较稳定。小T泡上一杯茶,正在浏览着天下大事,心里那个美啊!

“叮铃铃...叮铃铃...”,小T不仅被电话铃声吓了一跳,“谁啊,这么烦”,心里不禁咒骂了一句,不情愿地拿起了桌上的电话,“您好,请问哪位?”。

“小T,我是小S啊!出大事了,咱们的系统网络通信任务消失了,但是系统还在正常跑,其它任务也能工作。是不是咱们的操作系统出问题了啊?”听得出,小S现在是心急如焚。

听到这里,小T额头上的汗珠都下了。操作系统不能正常工作,这可是重大事故啊,弄不好又得被领导批,哎,“别急,把当时的工程代码发给我,我分析看看”小T对小S说。

拿到测试工程,加载运行。若干时间后,网络服务器与客户端的通信自动停止了。


3 确定问题


既然是网络任务不正常,那就先从网络开始分析吧!

首先,分析测试工程中对socket套接字使用;然后是,监控网络协议栈中的数据收发过程;最后是网络驱动程序的数据收发。然而,一无所获。

难道是进程调度算法有问题?于是,小T打印了系统运行中所有任务的调度过程,发现除了网络任务之外都是正常的,网络任务停止的时候,scheduler()调度器中确实没有了相关的进程信息。那说明,调度算法也没啥问题啊。到底是哪里的问题呢?

小T毕竟是一个有着过硬心理素质的人,怎么可能被这点问题吓到呢。他的信条就是,“没有解决不了的bug,如果有,肯定是遗漏了什么重要线索。”

于是,从头梳理。采用最简单粗暴的方式,在网络任务里采用标记法,确认任务停止调度的时候到底停在了哪里。通过打印信息发现,每次都停在了网络数据包(64K)进行CRC32校验的过程中。继续检查CRC32校验算法(本身算法已经用了好多年,不会出问题)。最终发现CRC校验算法中使用了除法/指令,计算需要计算CRC值的次数,正常的时候这个值是正确的;停止调度的时候,这个值是一个巨大的数值。本身这个值会被作为索引访问数组,导致越界,破坏了任务堆栈。

终于找到了根源,就好办了。小T查阅MIPS架构的除法指令,找到了这么一段内容:


A computed result written to the HI/LO pair by DIV, DIVU, MULT, or MULTU must be read by MFHI or MFLO before a new result can be written into either HI or LO. If an MTLO instruction is executed following one of these arithmetic instructions, but before an MFLO or MFHI instruction, the contents of HI are UNPREDICTABLE.

The following example shows this illegal situation:


MUL r2,r4 # start operation that will eventually write to HI,LO
...       # code not containing mfhi or mflo
MTLO r6
...       # code not containing mfhi
MFHI r3   # this mfhi would get an UNPREDICTABLE value


上面翻译成中文就是:


DIVDIVUMULTMULTU等乘除法指令,会将计算结果写入HI/LO这一对寄存器中,在下次做乘除法之前,必须使用指令MFHIMFLO将结果取走。如果在乘除算术指令和MFHIMFLO指令之间,调用MTLO指令写LO寄存器,那么HI寄存器的内容是不可预测的。

下面是非法情况的一个示例


MUL r2,r4 # r2和r4相乘,结果写入HI、LO
...       # 此处没有调用mfhi和mflo
MTLO r6   # 将r6的值写入LO
...       # 此处没有调用mfhi
MFHI r3   # r3的值不可预测


这就是CRC32校验中,计算的次数为什么会出现一个巨大值的原因:多任务系统中,调用乘除法指令的地方发生冲突,如果进程上下文切换中,没有保护HI/LO寄存器的内容,就会发生不可预料的结果。

小T马上review进程上下文切换的代码,发现确实只保护了通用寄存器,而忽略了这个特殊寄存器。

......(敲击键盘的清脆声,此起彼伏)

修改完代码,下载程序,启动可靠性测试后。小T陷入了深深的反思之中,暗暗懊悔自己的粗心大意,对文档的似是而非。

相关文章
|
6月前
|
存储
嵌入式微处理器的系统架构中指令系统
嵌入式微处理器的系统架构中指令系统
68 0
|
缓存 测试技术 数据中心
【计算机架构】计算 CPU 动态功耗 | 集成电路成本 | SPEC 基准测试 | Amdahl 定律 | MIPS 性能指标
【计算机架构】计算 CPU 动态功耗 | 集成电路成本 | SPEC 基准测试 | Amdahl 定律 | MIPS 性能指标
454 0
|
算法 编译器
【计算机架构】响应时间和吞吐量 | 相对性能 | 计算 CPU 时间 | 指令技术与 CPI | T=CC/CR, CC=IC*CPI
【计算机架构】响应时间和吞吐量 | 相对性能 | 计算 CPU 时间 | 指令技术与 CPI | T=CC/CR, CC=IC*CPI
1126 1
|
存储 并行计算 编译器
【计算机架构】程序指令计数 | 功耗计算 | 电力功耗 | 安德尔定律(Amdahl‘s Law)
【计算机架构】程序指令计数 | 功耗计算 | 电力功耗 | 安德尔定律(Amdahl‘s Law)
122 1
|
4月前
|
前端开发 Linux Shell
技术心得:基于AR9331(MIPS架构)分析系统启动过程(uboot)
技术心得:基于AR9331(MIPS架构)分析系统启动过程(uboot)
71 0
|
6月前
|
网络协议 Linux 测试技术
NFS - MIPS架构下构建NFS共享目录服务
NFS - MIPS架构下构建NFS共享目录服务
204 1
|
6月前
|
存储 编译器 数据处理
CPU架构和指令集
不同的CPU架构通常使用不同的指令集。每种CPU架构都有其自己的一组特定的机器指令,这些指令用于执行计算机程序。不同的CPU架构之间的指令集是不兼容的,这意味着编写的程序通常需要根据目标CPU的架构进行编译或汇编,以确保它们能够在该CPU上正确运行。 一些常见的CPU架构包括:
|
安全 Java
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
|
缓存 安全 Linux
MIPS架构深入理解9-向MIPS移植软件之Cache管理
MIPS架构深入理解9-向MIPS移植软件之Cache管理
|
存储 安全 编译器
MIPS架构深入理解11-向MIPS移植软件之编程语言
MIPS架构深入理解11-向MIPS移植软件之编程语言