c++ - 如何正确处理 SIGBUS 以便我可以继续搜索地址?

标签 c++ linux signals sigbus

我目前正在开展一个项目,该项目运行在经过大量修改的 Linux 版本上,该版本已打补丁以能够访问 VMEbus。大部分总线处理已完成,我有一个 VMEAccess 类,它使用 mmap 写入/dev/mem 的特定地址,以便驱动程序可以提取该数据并将其推送到总线上。

当程序启动时,它不知道它正在寻找的从属板在总线上的位置所以它必须通过四处寻找来找到它:它试图一个一个地读取每个地址,如果一个设备连接在那里read 方法返回一些数据,但如果没有任何连接,则 SIGBUS 信号将发送到程序。

我尝试了几种解决方案(主要是使用信号处理),但过了一段时间,我决定使用跳转。第一个 longjmp() 调用工作正常,但对 VMEAccess::readWord() 的第二次调用给我一个总线错误,即使我的处理程序应该防止程序崩溃。

这是我的代码:

#include <iostream>
#include <string>
#include <sstream>
#include <csignal>
#include <cstdlib>
#include <csignal>
#include <csetjmp>

#include "types.h"
#include "VME_access.h"

VMEAccess *busVME;

int main(int argc, char const *argv[]);
void catch_sigbus (int sig);
void exit_function(int sig);

volatile BOOL bus_error;
volatile UDWORD offset;
jmp_buf env;

int main(int argc, char const *argv[])
{
    sigemptyset(&sigBusHandler.sa_mask);

    struct sigaction sigIntHandler;

    sigIntHandler.sa_handler = exit_function;
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;

    sigaction(SIGINT, &sigIntHandler, NULL);

    /*   */
    struct sigaction sigBusHandler;

    sigBusHandler.sa_handler = catch_sigbus;
    sigemptyset(&sigBusHandler.sa_mask);
    sigBusHandler.sa_flags = 0;

    sigaction(SIGBUS, &sigBusHandler, NULL);

    busVME = new VMEAccess(VME_SHORT);

    offset = 0x01FE;

    setjmp(env);
    printf("%d\n", sigismember(&sigBusHandler.sa_mask, SIGBUS));

    busVME->readWord(offset);
    sleep(1);

    printf("%#08x\n", offset+0xC1000000);

    return 0;
}

void catch_sigbus (int sig)
{
    offset++;
    printf("%#08x\n", offset);
    longjmp(env, 1);
}

void exit_function(int sig) 
{
    delete busVME;
    exit(0);
}

最佳答案

如评论中所述,在信号处理程序中使用 longjmp 是个坏主意。跳出信号处理程序后,您的程序实际上仍在信号处理程序中。因此,调用非异步信号安全函数会导致未定义的行为。使用 siglongjmp 在这里并没有什么帮助,引用 man signal-safety:

If a signal handler interrupts the execution of an unsafe function, and the handler terminates via a call to longjmp(3) or siglongjmp(3) and the program subsequently calls an unsafe function, then the behavior of the program is undefined.

例如,这个 (siglongjmp) 在过去确实在 libcurl 代码中引起了一些问题,请参见此处:error: longjmp causes uninitialized stack frame

我建议改用常规循环并修改信号处理程序中的退出条件(无论如何都要修改那里的偏移量)。类似于以下内容(伪代码):

int had_sigbus = 0;

int main(int argc, char const *argv[])
{
    ...
    for (offset = 0x01FE; offset is sane; ++offset) {
        had_sigbus = 0;
        probe(offset);
        if (!had_sigbus) {
            // found
            break;
        }
    }
    ...
}

void catch_sigbus(int)
{
    had_sigbus = 1;
}

这样一来,循环就很明显了,整个逻辑也更容易理解。并且没有跳转,所以它应该适用于多个探测器 :) 但显然 probe() 也必须在内部处理失败的调用(被 SIGBUS 中断的调用) - 并且可能返回一个错误。如果确实返回错误,则可能根本不需要使用 had_sigbus 函数。

关于c++ - 如何正确处理 SIGBUS 以便我可以继续搜索地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47866913/

相关文章:

django - 在 Django 中提交事务后运行代码

c - Xlib 的全局热键

linux - 在 gdb 断点处运行命令,c 或 cont 都不起作用

multithreading - 运行可中断的 Rust 程序来生成线程

c - 使用信号的进程同步是如何工作的?

linux - ldd 在 x86 Linux 上显示不同的地址

c++ - 找到超过阈值的最小子集总和的线性算法

c++ - 制作 : c: command not found

分配 8GB 内存后,C++ stdlib.h system() 命令将无法运行

c++ - 列表迭代器不可递增