【问题标题】:How to correctly handle SIGBUS so I can continue to search an address?如何正确处理 SIGBUS 以便我可以继续搜索地址?
【发布时间】:2023-03-23 01:45:01
【问题描述】:

我目前正在处理一个项目,该项目在经过大量修改的 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);
}

【问题讨论】:

  • 不幸的是我不能,SIGBUS 是这种情况下定义的行为,因为我试图从不存在的板上读取我收到此错误,无法避免,至少在没有为 FPGA 创建我自己的驱动程序/内核的情况下是这样
  • Don't longjmp out of a signal handler。而是使用sigsetjmpsiglongjmp。请参阅the manual page 了解更多信息。
  • 哇!谢谢它的工作!其实很容易解决,非常感谢,你能用实际的答案按钮让我标记它已解决吗?

标签: c++ linux signals sigbus


【解决方案1】:

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

如果信号处理程序中断了不安全函数的执行,并且处理程序通过调用 longjmp(3) 或 siglongjmp(3) 终止,并且程序随后调用了不安全函数,则程序的行为未定义。

举个例子,这个 (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 函数。

【讨论】:

  • 我实际上已经尝试过这样的事情,但是由于某种原因,当程序探测到正确的地址时,它并没有退出循环。虽然当我以正确的偏移值启动程序时,它会退出。奇怪
  • 您确定在每个探测之前没有忘记将标志设置为0?
  • 我是的,当我尝试使用 cout 或 printf 查看发生了什么情况时,即使在 fflush 之后也不会打印任何内容
  • 听起来像是卡在探测功能的某个地方。很难说可能出了什么问题,但我怀疑错误处理不是很正确,因为这就是这里的区别。如果您从信号中跳转,则探测总线的函数只会在发送 SIGBUS 时停止执行。使用标志方法,一些系统调用将返回一个错误,并且控制将被传递回函数 - 它应该正确处理该错误。
  • 但是探测函数实际上依赖于内核中的一个函数(我不能在那里进行任何更改)所以我认为我被 siglongjmp “卡住”了(但是因为我编写了其他所有代码并且我不打算使用任何互斥锁或其他应该没问题的东西)
猜你喜欢
  • 1970-01-01
  • 2020-10-25
  • 1970-01-01
  • 2023-03-06
  • 2013-02-11
  • 1970-01-01
  • 2014-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多