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

如何从SIGBUS恢复?FPGA触发合法memcpy访问SIGBUS后的应用重置方案

FPGA通信场景下SIGBUS触发后的应用重置方案

问题背景

设计FPGA时,将合法memcpy访问mmap地址时偶尔产生的SIGBUS信号作为特殊通信机制,当前采用sigsetjmp/siglongjmp实现异常跳转重置,但遇到SIGBUS后程序会在memcpy处挂起,即使执行unmap后重新mmap,新地址上的memcpy仍会挂起。

问题根源

  1. 信号上下文残留:从信号处理函数用siglongjmp跳回后,memcpy的执行上下文处于中断异常状态,内核未完全清理该内存访问的异常标记,直接重试会重复触发问题。
  2. FPGA状态未复位:SIGBUS是FPGA主动触发的,说明此时FPGA处于特殊状态,仅操作内存映射无法解决硬件层面的异常。
  3. 代码参数错误:原代码中memcpy(mmapAddr, offset, data, sizeBytes)参数完全错误,memcpy仅接受3个参数,正确写法应为memcpy(static_cast<char*>(mmapAddr) + offset, data, sizeBytes),参数错误本身就会导致非法访问。

最佳重置方案

1. 修正信号处理与跳转逻辑

信号处理函数必须保证异步安全,且跳转前需确保信号掩码正常:

static sigjmp_buf jmpbuf;
static volatile int sigbus_triggered = 0;

void sigbus_handler(int signum) {
    // 异步安全输出,避免使用std::cout(非异步安全)
    const char notify[] = "[INFO] SIGBUS triggered, entering reset flow\n";
    write(STDERR_FILENO, notify, sizeof(notify)-1);
    sigbus_triggered = signum;
    // 跳回sigsetjmp保存的上下文
    siglongjmp(jmpbuf, signum);
}

2. 标准化重置流程

跳回sigsetjmp分支后,必须按硬件复位→清理映射→重建映射→状态初始化的顺序执行:

// 初始化信号处理
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigbus_handler;
sa.sa_flags = SA_NODEFER; // 允许嵌套触发SIGBUS(根据需求调整)
sigaction(SIGBUS, &sa, NULL);

// 核心访问逻辑
while (true) {
    if (sigsetjmp(jmpbuf, 1)) {
        // 步骤1:复位FPGA硬件状态(关键!优先处理硬件)
        memory.resetFPGA(); // 实现:向FPGA控制寄存器写入复位命令
        
        // 步骤2:清理旧内存映射
        if (memory.mmapAddr != MAP_FAILED) {
            munmap(memory.mmapAddr, memory.mmapSize);
            memory.mmapAddr = MAP_FAILED;
        }
        
        // 步骤3:重建内存映射
        if (!memory.recreateMmap()) {
            const char err[] = "[ERROR] Failed to recreate mmap\n";
            write(STDERR_FILENO, err, sizeof(err)-1);
            continue;
        }
        
        // 步骤4:初始化应用内部状态
        memory.fixThings();
        const char done[] = "[INFO] Reset completed, resuming operation\n";
        write(STDERR_FILENO, done, sizeof(done)-1);
    } else {
        // 正常执行内存写入(先修正memcpy参数)
        memory.writeMem(offset, dataPtr, size);
    }
}

3. 修正内存写入函数

修复memClass::writeMem的memcpy参数错误:

void memClass::writeMem(uint32_t offset, void* data, uint32_t sizeBytes) {
    // 确保mmapAddr有效,且偏移不超过映射范围
    if (mmapAddr == MAP_FAILED || (offset + sizeBytes) > mmapSize) {
        const char err[] = "[ERROR] Invalid memory access parameters\n";
        write(STDERR_FILENO, err, sizeof(err)-1);
        return;
    }
    // 正确计算目标地址:mmap基地址 + 偏移
    memcpy(static_cast<char*>(mmapAddr) + offset, data, sizeBytes);
}

4. 更可靠的替代方案(线程隔离)

如果sigsetjmp/siglongjmp的上下文残留问题难以解决,可采用线程隔离方案:

  • 将内存访问逻辑放到独立线程中,SIGBUS触发时直接终止该线程。
  • 主线程收到信号后,先复位FPGA,再重新创建线程并初始化内存映射,彻底清除异常上下文。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 23:35:22