正在进行的项目中有个日志存储模块,需要在设备端将日志数据写到存储介质——其实就是硬盘,就是一个文件。在我测试时,发现上位机读取到的日志数据不全,明明登陆到设备看有100多KB,但读到的日志才2行,肯定有问题,另外,设备存储的日志文件内容也有乱码出现。因为这个模块在其它项目一直使用,一直OK,现在出问题了,很郁闷。而且该模块嵌入到其它大的功能模块,后来跟踪、独立测试,终于找到问题原因。当这个“小”问题解决时,都已经过了大半天了,架构如果复杂,以至于要细细跟踪,不同平台的差别,以至于无人注意。
以前使用的是ARM平台,使用的存储介质是eeprom;而现在是X86平台,没有了eeprom,所有的数据,存储在硬盘上,而在linux上看,都是文件。这点,在项目初期,没什么人留意因平台差别而带来的影响、工作量——不要怪我,因为那时我正被内核和根文件系统搞得头大,而且又没参与架构代码的编写。
问题出现有2个原因,一是没注意eeprom和普通文件的操作上的区别,二是打开文件的框架代码封装得十分隐密(正因为封装得好,使人认为操作所有“文件”的代码都一样)。 日志模块抽象后的示例代码如下:
1 | // 打开文件 |
该模块使用文件前面的数据作为头部,保留了日志长度等关键信息。上位机读取才得到2行,是因为该头部中的日志长度字段除了第一次更新外,后面所有的写操作都没有更新——代码的确有更新“头部”,但即不是正在的头部,而是在日志数据后面添加。简单理解就是,最后的fseek并没有回到文件开头处,而是在文件结尾,于是fwrite就直接写到文件最后,这也解释了日志文件为什么会有乱码,因为那是头部数据。
查了fopen的参数,“a+”关键点如下:
Repositioning operations (fseek, fsetpos, rewind) affects the next input operations, but output operations move the position back to the end of file.
这句话最后是说光标都回到文件的末尾。因此,使用“a+”打开文件是不正确的。 网上有文章http://blog.csdn.net/flyfy1/article/details/4763347说得很好,就直接抄下来了:
1 | r+ 和 w+ 的区别: |
最终的解决方法是,将原来代码的“a+”改为“r+”,只改一个字符,即可解决问题。修改很简单,但知道修改的原因却要经过一番努力——而这,就是不为人知的背后的辛酸。
参考资料:
http://www.cplusplus.com/reference/cstdio/fopen/
http://blog.csdn.net/flyfy1/article/details/4763347
李迟,2015年1月17日 中午