打包msix格式的gtk程序运行后遇到了崩溃问题

⌚Time: 2025-06-26 11:27:00

👨‍💻Author: Jack Ge

我将gtk2.0程序打包成msix格式之后,在启动一个查看窗口,并且关闭后,有概率程序闪退崩溃。但是在未打包时是正常的,不会造成崩溃。

我使用GDB附加在运行程序上,在崩溃时得到了下面的输出

Program received signal SIGSEGV, Segmentation fault.
0x63a57c52 in signal_emit_unlocked_R ()
   from C:\Program Files\WindowsApps\xxx\libgobject-2.0-0.dll
(gdb) bt full
#0  0x63a57c52 in signal_emit_unlocked_R ()
   from C:\Program Files\WindowsApps\xxx\libgobject-2.0-0.dll
No symbol table info available.
#1  0x00000001 in ?? ()
No symbol table info available.
#2  0x00000001 in ?? ()
No symbol table info available.
#3  0x09db21a0 in ?? ()
No symbol table info available.
#4  0x00000000 in ?? ()
No symbol table info available.
(gdb) continue
Continuing.
[Thread 5064.0x20f0 exited with code 3221225477]
[Thread 5064.0x584 exited with code 3221225477]
[Thread 5064.0x8b8 exited with code 3221225477]
[Thread 5064.0xb04 exited with code 3221225477]
[Thread 5064.0x2a80 exited with code 3221225477]
[Inferior 1 (process 5064) exited with code 030000000005]
(gdb)

让deepsekk ai来找问题


这个错误表明你的程序在 libgobject-2.0-0.dll(GLib/GObject 库)中的 signal_emit_unlocked_R() 函数中触发了 段错误(Segmentation Fault, SIGSEGV)。这通常发生在 信号(signal)或回调(callback)处理过程中,可能的原因包括:

可能的原因

1. 信号(Signal)处理问题

2. 对象生命周期问题

3. 内存损坏

调试与解决方案

1. 获取崩溃时的调用栈(Backtrace)

在 GDB 中输入 bt full 查看完整的调用栈:

(gdb) bt full

重点关注:

2. 检查信号连接是否正确

确保:

示例代码:

// 错误示例:回调函数可能已被释放
g_signal_connect(button, "clicked", G_CALLBACK(callback_func), NULL);

// 正确做法:确保回调函数有效,对象未被释放
g_signal_connect_data(
    button, 
    "clicked", 
    G_CALLBACK(callback_func), 
    user_data, 
    NULL, 
    G_CONNECT_AFTER
);

3. 检查对象是否有效

在 GDB 中检查 GObject 指针:

(gdb) print *object
(gdb) print object->ref_count  # 查看引用计数

4. 检查多线程问题

如果信号在多个线程中触发:

5. 使用 GLib 调试工具

6. 捕获转储文件(Dump)

在 Windows 上使用 Procdump 捕获崩溃瞬间的转储文件:

procdump -ma -e -w your_program.exe

然后用 WinDBG 分析:

windbg -z crash.dmp

常见修复方案

错误现象 可能原因 解决方案
signal_emit_unlocked_R 崩溃 信号未连接/回调无效 检查 g_signal_connect 调用
GObject 已销毁 引用计数错误 检查 g_object_ref/g_object_unref
多线程竞争 回调函数非线程安全 使用 g_mutex_lock 保护数据

下一步建议

  1. 提供完整的 bt full 输出,方便定位具体问题。
  2. 检查信号连接代码,确保对象和回调有效。
  3. 使用 G_DEBUG=fatal_warnings 运行程序,查看 GLib 的警告信息。

如果仍有问题,请提供相关代码片段(特别是信号绑定和对象管理部分)。


之后我验证了它分析的是对的。

我用了模块屏蔽法。在该窗口的代码块屏蔽了一些东西,最后定位到一个语句,只要有它,就有概率崩溃,没有它,就不会崩溃

g_signal_connect(memoryViewVBox, "destroy", G_CALLBACK(g_free), data);

这个data是使用g_new0函数分配的一个结构体指针。我的意思是希望它随着窗口销毁的时候自动调用g_free释放。但是出了问题。

我询问deepseek ai,它果然指出了问题


你的代码分析

SearchData *data = g_new0(SearchData, 1);  // 分配内存并初始化为 0
g_signal_connect(
    memoryViewVBox, 
    "destroy", 
    G_CALLBACK(g_free),  // 这里有问题!
    data
);

问题

  1. G_CALLBACK(g_free) 是错误的用法

    • g_free 是一个普通函数,不是 GtkCallback 类型,不能直接用作信号回调。
    • 正确的做法是定义一个回调函数,在回调中调用 g_free
  2. data 的生命周期管理

    • 如果 memoryViewVBox 被销毁(如窗口关闭),"destroy" 信号会触发,但你的代码无法正确释放 data

正确做法

自定义回调函数释放内存

// 定义回调函数
static void on_memory_view_vbox_destroy(GtkWidget *widget, gpointer user_data) {
    SearchData *data = (SearchData *)user_data;
    g_free(data);  // 释放内存
}

// 连接信号
g_signal_connect(
    memoryViewVBox, 
    "destroy", 
    G_CALLBACK(on_memory_view_vbox_destroy),  // 使用自定义回调
    data
);

额外建议

  1. 检查 data 是否被其他地方引用

    • 如果 data 在其他地方被使用(如异步回调),确保不会出现 use-after-free
    • 可以使用 g_ref_count 或手动管理生命周期。
  2. 使用 GObject 的属性绑定

    • 如果 SearchData 是复杂结构,考虑改用 GObject 子类,利用 g_object_unref 自动管理内存。
  3. 调试内存泄漏

    • 使用 valgrindDr. Memory 检查是否完全释放:
      valgrind --leak-check=full ./your_program

如果仍有疑问,可以提供更多上下文(如 SearchData 的定义或完整信号连接逻辑)。


我写那条语句的时候,直接把一个普通函数g_free就当成回调函数用了,我还以为g_signal_connect强大到有未知办法来使用g_free

至于为什么在正常win32程序运行时,调试时没有崩溃,但是打包msix格式后安装就出现问题了,我也不知道

最后,感谢AI!