跳到主要内容

表面上的问题

问题描述与分析

今天同事请我帮忙解决一个问题。问题的表面现象是他在 take 一个信号量时系统会进入异常。我在看的时候他还在坚持是 take 信号量造成的问题,不过我对这一判断并不以为然。

要知道 take 信号量这种基础的操作会产生问题的可能性几乎为 0 。虽然程序确实是在执行到 take 信号量后进入到异常的,但这不过是表面现象,要说这一表面现象就是问题所在,我是不相信的。

解决问题所做的尝试

1. 排除栈溢出的问题

由于上一个月我刚遇到过一个栈溢出的问题,那个问题跟同事的问题的表现有相同之处。于是我首先怀疑栈溢出的问题。

为了确定是否真的是栈溢出的问题,我将程序的栈调到之前的数倍,重新运行程序,结果却发现问题仍旧存在,这就推翻了这一怀疑。

2. 观察 take 的信号量的数据成员

由于同事使用了阻塞的方式 take 一个信号量,因此调用此 take 语句的任务将会被挂起,其任务的 tcb 指针将从就绪表中移除并加入到待 take 信号量的等待链表中。如果在任务成功获取到信号量之前,信号量中的等待链表被意外修改,那么当任务要重新调度恢复执行时就会产生异常。

观察发现 take 信号量的等待链表指向了一个异常的地址,这是一个重要的发现。

3. 找到被意外修改的位置

这之后我们通过 watch 此信号量中的等待链表来找到意外修改发生的位置。很快我们便发现等待链表是在 memset 中被修改的,查看这个 memset 语句却并没有发现什么问题。

4. 真的是 memset 的问题?

调试到这里,我们可以说 memset 的函数调用导致了问题,可这样的说法也显得不太合理。

为了进一步确认,我们检查了 memset 的各个参数。在检查参数的过程中我们首先确认地址都是正确的,然后我们怀疑可能是大小有问题。可是这个大小是用 sizeof 计算的结构体大小,应该是不容置疑的。

5. 问题的真相

这样看来 memset 好像没有问题啊!不过我们还是通过打印结构体首尾元素的地址,手动计算了下结构体的大小。到了这一步我们终于发现了问题的根源。

sizeof 得到的大小比结构体的大小多出了很多字节。sizeof 是在编译期间确定的值,那么我们已经可以断定这是头文件错误包含导致的问题。

检查发现代码中包含了一个错误的头文件,这个头文件恰好是同事正在修改的头文件,他为了备份创建了两个头文件,在另外一个头文件中进行修改,却没有用新的头文件替换旧头文件的包含语句。

总结

在修改现有的代码时我们可能会创建一份当前代码的备份,然后在备份中修改代码,在修改完成之前在工程中保留原来的代码。这样的做法很容易造成混乱,不值得提倡。

现在的项目几乎都会使用项目管理工具来管理。这样你完全可以直接将旧的代码移除,重新创建新的代码。既不用担心丢失旧代码,又能够避免混乱造成的各种隐含问题。何乐而不为呢?