有一种bug叫升级操作系统的bug linuxcentos7memcpy
前一阵子,公司的一款SLG游戏要到海外上线。这款游戏的服务器程序原先是在centos6的环境中开发并运行在centos6上的,一组服主要由网关服和逻辑服组成。其中网关服接受客户端的tcp长连接,并转发客户端的请求消息到逻辑服,逻辑服处理好逻辑传给网关服,再由网关传回客户端。之前国服上线前运营方曾要求我们部署到centos7上,他们认为centos7会比centos6更安全运行效率更高。但考虑时间紧我们没迁移。于是等到这次海外上线时,我们准备把整套服务器代迁移到centos7上开发。编译部署很顺利,之后的内部测试也很顺利没出现什么异常。
然而游戏上线不久就发现客户端时不时地会收不到服务器的响应消息,查看客户端和网关服的链接,发现没问题,此时客户端还能发送请求消息到网关服。难道逻辑服挂了?登上服务器查看服务器进程,发现逻辑服进程还在,运行也正常。此刻,网关服接受客户端的消息正常,逻辑服也在工作,就是无法把响应消息发给客户端。进一步排查,我发现这时逻辑服能够收到来自网关服的消息,也能正确处理逻辑并把结果传给网关服,网关服的确也接受到了来自逻辑服的消息,就是没有再把这些包转发给客户端。很明显,问题应该出在网关服的网络通信层。继续查看网关服的网络收发日志,发现网关服收到的消息头中标示消息长度那个字段错乱了,这导致了后续消息再也无法正确解析,于是之后的来自逻辑服的消息再也无法转发给客户端了。
是什么原因导致消息头错乱呢?我初步分析可能的原因有两个:一是逻辑服发送消息时消息头已错乱,这种错误极有可能是本次新加的逻辑导致;二是逻辑服发送的消息没问题,网关接收时产生了错乱,这种错误极有可能底层库中网络通信模块有bug导致的。由于这个底层库我开发使用至今有7年了,并且国服之前一直运行地很稳定,所以我首先怀疑是新加的逻辑造成的。很不幸运,一番调试下来发现真不是新加逻辑导致的。难道是底层库中一直隐藏了这个bug?同样调试了一遍又一遍后,感觉貌似也不是底层的bug。毕竟这个库我已经使用了好几个项目了。
至此修复这个bug陷入困镜,我没了思路,只能修改代码,在可能导致bug的地方添加更详细的日志,然后当bug出现后排查之前之后的日志。通过一遍又一遍地排查日志,我终于发现一些bug出现的规律。首先出现这个bug总是出现在逻辑服向网关服发送大量的数据时。这种情况下为了给其他逻辑留点cpu时间,底层库只处理一定量的消息,剩余消息留给下一帧处理。国服和之前经历的项目都这么做的,没出啥问题。并且这次网关服收到一帧处理不了的大包时,也不是必出bug,而是有时出现,有时不出现。其次我发现出现bug前,网关服收到的消息类型似乎就那几种类型。我在逻辑服排查了产生那几条消息的代码,发现这些逻辑中有些设置google protocol buffer 的代码写得不太好,难道是误用这个库导致的?我优化了这部分代码,然后再调试。很可惜,那个bug仍旧顽强地存在着。
我只能回头继续从处理大包的底层代码去排查,继续加日志,继续查日志。排查来排查去,我又有了新发现:起初网关服收到大包时,数据都对的,但等到下一帧处理时数据又错了,很明显导致bug的代码位于将大包中没处理的消息移动到缓冲区顶部的代码中。进一步排查这部分代码,很遗憾仍旧没啥新发现,这部分代码好几年前就写好了,已经稳定运行了好几年了。无奈之下,我把焦点集中到了调用移动数据的那个libc函数memcpy上,因为这个逻辑最后一步就是调用这个函数的。难道这个函数有问题?不可能啊,这个可是系统提供的函数啊?我可以相信自己写的逻辑代码会出bug,自己写的底层库也会出bug,还有误用第三方库也会,但是系统提供的函数怎么可能出问题?毕竟centos这个操作系统的稳定性可是有口皆碑的。可是到目前为止我也实在找不出其他的原因,抱着死马当活马医的心态,我网上查了一下关于这个函数的资料。不看不知道,一看吓一跳,果然这个函数有点问题,原来使用这个函数时,当目标地址和源地址是处于同一块缓存时,拷贝的数据可能出错。这种变化正是升级到centos7之后才发生的。原来如此,这就解释了为啥之前在centos5,centos6上使用这个库写的服务器程序没这个问题,而升级到了centos7后才出了问题。也解释了为啥某些逻辑容易催发这个Bug,因为在这些逻辑中会催发大量的广播消息,这样逻辑服发给网关服会出现大量的大包。找到了确切的原因后,后面的修复就顺风顺水了,网上对我这种应用场景给出的建议是使用memmove函数替代,我照着做了,然后编译部署测试。终于这个困扰了我三天多的bug被彻底修复了。
随着这个Bug的修复,我感觉自己的修服务器程序bug的经验值又增长了一大截。并且在我的修bug的知识库中又多了一种bug类型,叫做升级操作系统的bug!
上一篇: 巨龙之魂难度再次削弱达到10%
下一篇: mysql全文索引