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

问题描述 投票:1回答:1

我目前正在使用一个经过大量修改的Linux版本的项目,所以它可以使用我们公司需要的一些非常旧的总线。大多数总线处理都已完成,我有一个VMEAccess类,它使用mmap在/ dev / mem的特定区域写入,这样驱动程序就可以提取数据并将其推送到总线上。

我的问题是,当程序启动时,它不会知道从板的位置,我可以拥有的唯一信息是:如果给定地址没有任何内容,那么读取会产生SIGBUS(总线错误)所以我决定使用它来找到奴隶(基本上尝试所有的地址,直到我不再有SIGBUS)

我尝试了几件事,主要是使用信号处理,但经过一些尝试,我正在使用一些跳跃。第一个longjmp()工作,但第二次读取给我一个总线错误,即使我的处理程序应该阻止程序崩溃。

我的main.cpp:

#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);
}
c++ linux signals sigbus
1个回答
0
投票

正如评论中所提到的,在信号处理程序中使用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函数返回错误,则根本不需要。

© www.soinside.com 2019 - 2024. All rights reserved.