• 一个QT程序无法启动问题的分析与解决


    最近调试设备,遇到了一个奇怪的问题:QT程序无法重启。

    查看日志,发现报如下错误:

    QLock::QLock: Cannot create semaphore /tmp/qtembedded-0/QtEmbedded-0 'd' (22, Invalid argument)
    Cannot get display lock
    Aborted

    下面整理记录问题的解决过程。

    1 首先,说明程序之前奔溃了。
    因为这是重启过程中报的信息。后台监控发现程序奔溃后,会再次拉起。这个日志就是拉起过程中出现的。

    2 先不管之前为啥崩溃了。我们看看程序为啥无法成功重启。

    3 是否是因为程序本身出了什么问题?
    因为这个错误之前不曾见过,又因为之前出现过flash上的文件发生损坏的情况,所以猜测会不会是因为坏块导致的执行程序文件损坏。
    通过将程序二进制文件拷贝出来以及将新的替换进去,验证程序没有发生变化,但是问题一直存在。
    使用其他QT程序,问题也依然存在,所以程序本身的问题被排除

    4 是否是因为QT基础库问题导致?
    基本思路还是跟3中描述一样,只不过这次怀疑是否是QT的基础库发生了变动。
    如果基础库发生变动,那么所有QT程序都可能无法正常运行。
    将设备上的QT相关库拷贝出来,跟烧写版本对比,发现基础库没有变化。
    重新替换QT基础库,问题仍然存在。

    5 是否是因为QT运行环境问题导致?
    拷贝异常设备中,root tmp等目录下QT生成的文件,跟正常设备对比,发现没有明显的差异

    6 查找错误日志所在代码
    既然前面几个怀疑点都排除了,没有明确的验证方向的情况下,决定看看错误日志到底是那块代码打印的。
    通过搜索QT程序和QT开发环境,发现Cannot get display lock这一句是QT基础库里的打印
    进一步的,确定了代码所在位置:qtapplication_qws.cpp.

    跟踪代码,发现是qtlock初始化失败了。
    但是,相关的代码有很多编译选项,不确定错误到底是那个if else逻辑出来的。
    考虑到整个QT自身的代码比较庞大,搜索也不容易确定宏是否是打开状态,决定添加日志,跟踪定位问题。

    7 重新编译QT库
    重新编译QT库后,将日志所在的GUI库替换,重新跑程序来看:
     

    1. QLock::lock(): file name /tmp/qtembedded-0/QtEmbedded-0 id=d create=create
    2. QLock::lock(): QT_POSIX_IPC
    3. QLock::lock(): QT_POSIX_IPC 1 data id = -1 semkey=1678592551 (2, No such file or directory)
    4. QLock::lock(): QT_POSIX_IPC 2 data id = -1 semkey=1678592551, (28, No space left on device)
    5. QLock::lock(): QT_POSIX_IPC 3 data id = -1 arg.val=200, (22, Invalid argument)
    6. QLock::QLock: Cannot create semaphore /tmp/qtembedded-0/QtEmbedded-0 'd' (22, Invalid argument)
    7. Cannot get display lock
    8. Aborted

    这是最后确定问题的日志,中间过程不再说明。
    我们看到,走了IPC处理分支
    关键错误在第四行,errno是28,说明没有空间
    这句日志对应的代码接口为semget

    8 查看系统调用说明
    man semget,查看这个系统调用的使用说明。
    其中有关于28错误的说明,ENOSPC,基本是说创建信号量时,达到了系统配置的上限,没有空间创建新的。
    到这里,基本可以猜出问题所在了。就是程序之前可能反复重启,消耗了所有的信号量空间,达到一定次数后,无法创建新的信号量,导致启动失败。

    9 查看系统配置参数
    查看系统对信号量 共享内存等的配置
     # cat /proc/sys/kernel/sem 
      250     32000   32      128

    可以看到,信号量给的是128个。
    具体查看系统中创建的信号量

    1. # cat /proc/sysvipc/sem
    2. key semid perms nsems uid gid cuid cgid otime ctime
    3. 1678576641 32768 600 1 0 0 0 0 1649596772 43
    4. 1678623274 65537 600 1 0 0 0 0 1649596832 1649596774
    5. 1678624879 4259842 600 1 0 0 0 0 1649606005 1649605937
    6. 1678625856 131075 600 1 0 0 0 0 1649596973 1649596905
    7. ...
    8. 1678593752 4030586 600 1 0 0 0 0 1649604774 1649604707
    9. 1678595070 4063355 600 1 0 0 0 0 1649604848 1649604776
    10. 1678596881 4096124 600 1 0 0 0 0 1649604917 1649604849
    11. 1678598116 4128893 600 1 0 0 0 0 1649604986 1649604918
    12. 1678599937 4161662 600 1 0 0 0 0 1649605054 1649604987
    13. 1678598220 4358271 600 1 0 0 0 0 1649964888 1649964880

    统计一下,发现达到了上限。这里多的1是第一行,用于说明各个列段含义的行
      # cat /proc/sysvipc/sem  | wc -l
      129

    10 验证
    删除一个信号量,重启程序,可以看到重启成功

    # ipcrm -s 4194431

    1. QLock::lock(): file name /tmp/qtembedded-0/QtEmbedded-0 id=d create=create
    2. QLock::lock(): QT_POSIX_IPC
    3. QLock::lock(): QT_POSIX_IPC 1 data id = -1 semkey=1678598220 (2, No such file or directory)
    4. QLock::lock(): QT_POSIX_IPC 2 data id = 4358271 semkey=1678598220, (2, No such file or directory)
    5. QLock::lock(): QT_POSIX_IPC 3 data id = 4358271 arg.val=200, (2, No such file or directory)

    关闭程序,再次重启,看到失败,说明问题就是由于空间限制,导致信号量创建失败产生

    1. QLock::lock(): file name /tmp/qtembedded-0/QtEmbedded-0 id=d create=create
    2. QLock::lock(): QT_POSIX_IPC
    3. QLock::lock(): QT_POSIX_IPC 1 data id = -1 semkey=1678598276 (2, No such file or directory)
    4. QLock::lock(): QT_POSIX_IPC 2 data id = -1 semkey=1678598276, (28, No space left on device)
    5. QLock::lock(): QT_POSIX_IPC 3 data id = -1 arg.val=200, (22, Invalid argument)
    6. QLock::QLock: Cannot create semaphore /tmp/qtembedded-0/QtEmbedded-0 'd' (22, Invalid argument)
    7. Cannot get display lock
    8. Aborted

    11 进一步的研究
    根据代码来看,每次创建信号量的ftok函数调用参数都是一样的,但是为啥QT每次打印出来的id不一样呢。
    因为不一样,所以每次创建的总是保留着,直到空间用完。
      
    但是根据接口说明,ftok同样的参数,生成的结果是一样的。专门写了一个程序验证了一下:
      /tmp # /mnt/a.out 
      semkey is 1678611420 
      /tmp # /mnt/a.out 
      semkey is 1678611420 
      /tmp # /mnt/a.out 
      semkey is 1678611420 
      
    为啥QT创建的不一样呢?
    我们重新创建文件,再跑程序,可以看到生成的不一样了。
    ftok是根据的文件的inode信息来生成id的。
      /tmp # rm /tmp/qtembedded-0/QtEmbedded-0
      /tmp # touch /tmp/qtembedded-0/QtEmbedded-0
      /tmp # /mnt/a.out 
      semkey is 1678611403 
      
    因此,QT里是每次新建了文件导致id不一样了。对此做针对性修改,问题即解决。

    12:其他

    涉及的代码文件为:
    qt-everywhere-opensource-src\src\gui\kernel\qappliction_qws.cpp
    qt-everywhere-opensource-src\src\gui\embedded\qlock.cpp

  • 相关阅读:
    【Linux指令】Centos7 touch修改Access/Modify/Change 时间与恢复系统时间
    如何在Window系统部署BUG管理软件并结合内网穿透实现远程管理本地BUG
    git项目删除业务代码、并清除所有提交记录,以此为基础创建出一个干净仓库、再另建一个远程代码库推上去
    vos网络电话系统搭建
    ZMQ/ZeroMQ的三种消息模式
    10.DesignForSymbols\Symboldownto15.51...
    MATLAB程序设计与应用 3.3 矩阵求值
    【QT】QtConcurrent的使用介绍,与std::thread的区别
    Linux下安装MySQL你又踩过多少坑【宇宙最全教程】
    lua快速入门~在js基础上,知道Lua 和 Js 的不同即可
  • 原文地址:https://blog.csdn.net/wwwyue1985/article/details/134221509