Как правильно обрабатывать SIGBUS, чтобы я мог продолжить поиск адреса?

В настоящее время я работаю над проектом, использующим сильно модифицированную версию 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);
}

1 ответ

Решение

Как уже упоминалось в комментариях, используя longjmp в обработчике сигналов это плохая идея. После выполнения прыжка из обработчика сигнала ваша программа по-прежнему остается в обработчике сигнала. Таким образом, вызов не асинхронно-безопасных функций приводит, например, к неопределенному поведению. С помощью siglongjmp не очень поможет здесь, цитируя man signal-safety:

Если обработчик сигнала прерывает выполнение небезопасной функции, а обработчик завершается с помощью вызова longjmp(3) или siglongjmp(3), и программа впоследствии вызывает небезопасную функцию, то поведение программы не определено.

И только для примера, это (siglongjmp) в прошлом вызывал некоторые проблемы в коде libcurl, смотрите здесь: error: longjmp вызывает неинициализированный кадр стека

Я бы предложил использовать обычный цикл и изменить условие выхода в обработчике сигнала (вы в любом случае изменяете смещение там). Что-то вроде следующего (псевдокод):

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 функция может быть не нужна вообще.

Другие вопросы по тегам