如何正确处理 SIGBUS 以便我可以继续搜索地址?
How to correctly handle SIGBUS so I can continue to search an address?
我目前正在开展一个项目 运行,该项目是对 Linux 进行大量修改的版本,该版本已修补以能够访问 VMEbus。大部分总线处理已完成,我有一个 VMEAccess class,它使用 mmap 写入特定地址 /dev/mem,因此驱动程序可以提取该数据并将其推送到总线上。
当程序启动时,它不知道它正在寻找的从属板在总线上的位置,因此它必须通过四处寻找来找到它:如果连接了设备,它会尝试一个一个地读取每个地址有读取方法 returns 一些数据,但如果没有任何连接,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
中断的调用)- 可能 return一个错误。如果它确实 return 使用 had_sigbus
函数的错误可能根本没有必要。
我目前正在开展一个项目 运行,该项目是对 Linux 进行大量修改的版本,该版本已修补以能够访问 VMEbus。大部分总线处理已完成,我有一个 VMEAccess class,它使用 mmap 写入特定地址 /dev/mem,因此驱动程序可以提取该数据并将其推送到总线上。
当程序启动时,它不知道它正在寻找的从属板在总线上的位置,因此它必须通过四处寻找来找到它:如果连接了设备,它会尝试一个一个地读取每个地址有读取方法 returns 一些数据,但如果没有任何连接,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
中断的调用)- 可能 return一个错误。如果它确实 return 使用 had_sigbus
函数的错误可能根本没有必要。