自学内容网 自学内容网

一台工控机出现C++工程线程卡住问题的排查

某 x86 工控机在现场升级后无法正常连接,业务无法响应,工控机界面卡住。经较短时间分析并排查,解决了问题,虽然没有很难的技术问题,但过程还是值得记录的。

由于本文没有技术含量,请谨慎按需阅读。

起因

某天晚上近8点下班,骑电驴回家,路上,同事发了一个问题截图,现场反馈升级后机器卡住,无法正常工作。截图有pstack输出的结果,初步看和我负责的一个模块有关,但看不出具体问题,后回复同事说明早看看。

排查及解决

问题定位

观察截图,进程有多个线程,涉及第三方厂家库,涉及Qt线程库、pthread线程库。信息较多,线索不好清理,但关键的问题,是找到问题的关键,本问题的关键在pthread_mutex_lock__lll_lock_wait这2个关键字。——还好,这次不需要像年初那样找_gcry_ath_mutex_lockath_mutex_t关键字的源码。

根据关键字找到对应的模块代码,发现初始化和反初始化都用到了同一mutex锁,从代码可以看到如初始化失败,会调用反初始化函数,由于反初始化函数也用了锁,但此时初始化未完成退出。因此,按初始化失败的流程走,是会锁住的。具体见下面的现场重演。

由于该模块一直能正常工作,本次升级的多台设备均未出现,从初始化失败的条件反推,有可能是所需要的外部文件出错。于是登陆设备,查看相关文件的md5,果然有料到,某一sqlite3数据库文件的md5与原版的不同,下载到本地后,用数据库工具打开,提示database disk image is malformed。再用工具对比,错误文件的后半部分缺了,体积少了一半,但奇怪的是半部分是完全相同的,相当于被不可抗拒的力量裁为两截了。

因此,导致本次问题的原因是:升级过程中,数据库文件被破坏了,具体过程并不知晓,但无论怎么讲,知道原因后,解决就方便很多了。

解决方法

重新下载升级包进行升级即可。

现场重演

下面模拟实际工程的函数布局,舍去无关的代码,以突显问题。

注:将ret改为负数即可重现问题。

/*
线程锁测试
编译命令:
g++ mutex_test.cpp -lpthread
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <pthread.h>

static pthread_mutex_t module_lock = PTHREAD_MUTEX_INITIALIZER;

void uninit()
{
pthread_mutex_lock(&module_lock);

    printf("un init something.\n");
    
    pthread_mutex_unlock(&module_lock);
}

void init()
{
    int ret = 0;

    printf("init start.\n");
pthread_mutex_lock(&module_lock);

    // do something need and get return value
    
    ret = -1; // get wrong here
    if (ret < 0) {
        uninit();
    }

    pthread_mutex_unlock(&module_lock);

    printf("init done.\n");
}

int main(void)
{
    init();
    return 0;
}

在虚拟机的测试结果:

$ ./a.out 
init start.

使用ps命令查看进程PID,再用pstack查看,结果如下:

$ ps aux | grep a.out
fengxuan  13971  0.0  0.0  14704   860 pts/2    S+   09:50   0:00 ./a.out
fengxuan  13973  0.0  0.0 112828   996 pts/1    S+   09:50   0:00 grep --color=auto a.out
$ pstack 13971
#0  0x00007f8389dd054d in __lll_lock_wait () from /lib64/libpthread.so.0
#1  0x00007f8389dcbe9b in _L_lock_883 () from /lib64/libpthread.so.0
#2  0x00007f8389dcbd68 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3  0x000000000040062b in uninit() ()
#4  0x0000000000400676 in init() ()
#5  0x0000000000400695 in main ()

上述输出结果,和现场的不能说一模一样,只能说前3行的pthread库的函数一模一样。

小结

由于只一台设备出现问题,不具有代表性,但对于升级过程的文件完整性保证,还是需要做的。由于非个人范围,因此只是做了些建议。


原文地址:https://blog.csdn.net/subfate/article/details/143587574

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!