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

2022-08-15 16:27:15 浏览数 (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:

代码语言:javascript复制
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寄存器的内容是不可预测的。

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

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

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

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

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

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

0 人点赞