You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CentOS7移植代码到Rocky Linux8触发GLib断言失败崩溃问题

问题根因
  • 报错触发的断言(i <= 0 || fds[i - 1].fd < fds[i].fd)是GLib主循环在执行poll前的强校验,要求所有待监听的文件描述符必须严格按fd数值升序排列、无重复项。
  • 版本差异是问题出现的核心诱因:CentOS 7 预装的GLib版本为2.50.3,该版本内部会自动对传入的pollfd列表做排序、去重,没有该校验逻辑,不规范的代码不会触发崩溃;Rocky Linux 8预装的GLib版本为2.56.4,新增了该断言校验,移除了部分场景下的自动排序逻辑,不符合要求的fd列表会直接触发进程abort。
  • 从strace捕获的poll系统调用参数可以直接看到异常:待监听的fd列表顺序为1、5、3、4,其中fd=5大于后续的fd=3、fd=4,完全不满足升序要求,直接触发断言。
  • 问题源头是自定义GSource实现(对应capture_msgs_funcs结构体):代码中创建了自定义GSource挂载到默认主上下文,该GSource负责向主循环返回待监听的pollfd列表,其返回的fd列表存在乱序、重复追加的问题。另外代码中开启了g_source_set_can_recurse(source, TRUE),如果递归调度主循环时重复追加fd,也会加剧该问题。
排查步骤
  • 第一步:注释掉自定义GSource的attach相关逻辑,重新运行程序,如果崩溃消失,即可确认问题出在该自定义GSource的实现上。
  • 第二步:定位capture_msgs_funcs结构体中对应的query回调(GSourceFuncs的第三个函数指针,负责返回待监听的fd集合),检查fd写入逻辑:
    • 是否存在同一个fd被多次追加到poll数组的情况
    • 是否未对追加的fd按数值从小到大做排序
  • 第三步:检查是否存在多线程无锁操作默认主上下文的情况:GLib默认主上下文不是线程安全的,其他线程未持有上下文锁就追加GSource、修改pollfd列表,也会导致fd列表顺序被打乱。
修复方案
  1. 修正自定义GSource的query回调逻辑,保证返回的pollfd列表无重复、严格升序:
    参考实现逻辑如下:
    // 假设你已将所有需要监听的fd存入临时数组tmp_fds,有效长度为cnt
    // 1. 先对fd做去重
    int dedup_len = 0;
    for (int i = 0; i < cnt; i++) {
        int duplicated = 0;
        for (int j = 0; j < dedup_len; j++) {
            if (tmp_fds[j] == tmp_fds[i]) {
                duplicated = 1;
                break;
            }
        }
        if (!duplicated) {
            tmp_fds[dedup_len++] = tmp_fds[i];
        }
    }
    // 2. 对去重后的fd做升序排序
    for (int i = 0; i < dedup_len; i++) {
        for (int j = i + 1; j < dedup_len; j++) {
            if (tmp_fds[i] > tmp_fds[j]) {
                int swap_tmp = tmp_fds[i];
                tmp_fds[i] = tmp_fds[j];
                tmp_fds[j] = swap_tmp;
            }
        }
    }
    // 3. 将排序去重后的fd写入GLib要求的pollfd数组
    *n_fds = dedup_len;
    for (int i = 0; i < dedup_len; i++) {
        fds[i].fd = tmp_fds[i];
        fds[i].events = G_IO_IN; // 按实际业务需要设置监听事件
        fds[i].revents = 0;
    }
    
  2. 规范主上下文的多线程操作:所有涉及主上下文、GSource的修改操作,必须放在g_main_context_acquire()和g_main_context_release()包裹的临界区内,禁止跨线程无锁操作默认主循环。
  3. 移除不必要的递归配置:如果业务不需要递归调度主循环,删除g_source_set_can_recurse(source, TRUE)这行配置,避免递归迭代时重复追加pollfd。
临时调试规避方案(不推荐生产环境使用)

可以通过设置环境变量G_MAIN_CONTEXT_POLL_SORT=1,强制GLib内部对待监听的pollfd列表做自动排序、去重,跳过断言检查。该参数不是所有GLib小版本都支持,且会带来额外性能开销,仅用于临时定位问题,正式环境必须修正代码逻辑。

内容的提问来源于stack exchange,提问作者ctsuzuki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 20:18:18