崩溃后程序保持运行状态而不退出
2016-09-21 22:24
369 查看
参考:
http://blog.csdn.net/langresser_king/article/details/8288195
http://stackoverflow.com/questions/2663456/how-to-write-a-signal-handler-to-catch-sigsegv
When your signal handler returns (assuming it doesn't call exit or longjmp or something that prevents it from actually returning), the code will continue at the point the signal occurred, reexecuting the same instruction. Since at this point, the
memory protection has not been changed, it will just throw the signal again, and you'll be back in your signal handler in an infinite loop.
So to make it work, you have to call mprotect in the signal handler. Unfortunately, as Steven Schansker notes, mprotect is not async-safe, so you can't safely call it from the signal handler. So, as far as POSIX is concerned, you're screwed.
Fortunately on most implementations (all modern UNIX and Linux variants as far as I know), mprotect is a system call, so is safe to call from within a signal handler, so you can do most of what you want. The problem is that if you want to change
the protections back after the read, you'll have to do that in the main program after the read.
Another possibility is to do something with the third argument to the signal handler, which points at an OS and arch specific structure that contains info about where the signal occurred. On Linux, this is a ucontext structure, which contains machine-specific
info about the $PC address and other register contents where the signal occurred. If you modify this, you change where the signal handler will return to, so you can change the $PC to be just after the faulting instruction so it won't re-execute after the handler
returns. This is very tricky to get right (and non-portable too).
The ucontext structure is defined in <ucontext.h>. Within the ucontext the field uc_mcontext contains the machine context, and within that, the array gregs contains the general register context. So in your signal handler:
ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];
will give you the pc where the exception occurred. You can read it to figure out what instruction it was that faulted, and do something different.
As far as the portability of calling mprotect in the signal handler is concerned, any system that follows either the SVID spec or the BSD4 spec should be safe -- they allow calling any system call (anything in section 2 of the manual) in a signal
handler.
参考《unix高级环境编程》
while(1)
{
registerExceptionCatcher();
if(sigsetjmp(jmpbuf, 1))
{
__DEBUG_LOG("jump back here #594");
registerExceptionCatcher();
continue;
}
canjump = 1;
// 可能崩溃的代码
unregisterExceptionCatcher();
}
static void sighandler (int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;
static void sighandler(int signum)
{
if (0 == canjump)
{
__DEBUG_LOG("canjump 0");
return;
}
struct tm *t = NULL;
time_t stm;
time(&stm);
t = localtime(&stm);
char timeBuf[64] = {0};
if(t != NULL)
{
snprintf(timeBuf,50,"%s %d-%02d-%02d %02d:%02d",t->tm_zone,t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min);
}
__DEBUG_LOG("[crash] sighandler signum:%d, %s", signum, timeBuf);
canjump = 0;
siglongjmp(jmpbuf, 1); /* jump back to the line before crash */
}
static struct sigaction act, oact;
void registerExceptionCatcher()
{
memset(&act, 0, sizeof(sigaction));
act._u._sa_handler = sighandler;
act.sa_flags = SA_RESETHAND;
sigemptyset(&act.sa_mask);
sigaction(SIGSEGV, &act, &oact);
}
http://blog.csdn.net/langresser_king/article/details/8288195
http://stackoverflow.com/questions/2663456/how-to-write-a-signal-handler-to-catch-sigsegv
When your signal handler returns (assuming it doesn't call exit or longjmp or something that prevents it from actually returning), the code will continue at the point the signal occurred, reexecuting the same instruction. Since at this point, the
memory protection has not been changed, it will just throw the signal again, and you'll be back in your signal handler in an infinite loop.
So to make it work, you have to call mprotect in the signal handler. Unfortunately, as Steven Schansker notes, mprotect is not async-safe, so you can't safely call it from the signal handler. So, as far as POSIX is concerned, you're screwed.
Fortunately on most implementations (all modern UNIX and Linux variants as far as I know), mprotect is a system call, so is safe to call from within a signal handler, so you can do most of what you want. The problem is that if you want to change
the protections back after the read, you'll have to do that in the main program after the read.
Another possibility is to do something with the third argument to the signal handler, which points at an OS and arch specific structure that contains info about where the signal occurred. On Linux, this is a ucontext structure, which contains machine-specific
info about the $PC address and other register contents where the signal occurred. If you modify this, you change where the signal handler will return to, so you can change the $PC to be just after the faulting instruction so it won't re-execute after the handler
returns. This is very tricky to get right (and non-portable too).
The ucontext structure is defined in <ucontext.h>. Within the ucontext the field uc_mcontext contains the machine context, and within that, the array gregs contains the general register context. So in your signal handler:
ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];
will give you the pc where the exception occurred. You can read it to figure out what instruction it was that faulted, and do something different.
As far as the portability of calling mprotect in the signal handler is concerned, any system that follows either the SVID spec or the BSD4 spec should be safe -- they allow calling any system call (anything in section 2 of the manual) in a signal
handler.
参考《unix高级环境编程》
while(1)
{
registerExceptionCatcher();
if(sigsetjmp(jmpbuf, 1))
{
__DEBUG_LOG("jump back here #594");
registerExceptionCatcher();
continue;
}
canjump = 1;
// 可能崩溃的代码
unregisterExceptionCatcher();
}
static void sighandler (int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;
static void sighandler(int signum)
{
if (0 == canjump)
{
__DEBUG_LOG("canjump 0");
return;
}
struct tm *t = NULL;
time_t stm;
time(&stm);
t = localtime(&stm);
char timeBuf[64] = {0};
if(t != NULL)
{
snprintf(timeBuf,50,"%s %d-%02d-%02d %02d:%02d",t->tm_zone,t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min);
}
__DEBUG_LOG("[crash] sighandler signum:%d, %s", signum, timeBuf);
canjump = 0;
siglongjmp(jmpbuf, 1); /* jump back to the line before crash */
}
static struct sigaction act, oact;
void registerExceptionCatcher()
{
memset(&act, 0, sizeof(sigaction));
act._u._sa_handler = sighandler;
act.sa_flags = SA_RESETHAND;
sigemptyset(&act.sa_mask);
sigaction(SIGSEGV, &act, &oact);
}
相关文章推荐
- Linux 命令,如何让运行一个jar程序,当控制台退出后,仍然保持运行状态
- 重新想象 Windows 8 Store Apps (69) - 其它: 自定义启动屏幕, 程序的运行位置, 保持屏幕的点亮状态, MessageDialog, PopupMenu
- 重新想象 Windows 8 Store Apps (69) - 其它: 自定义启动屏幕, 程序的运行位置, 保持屏幕的点亮状态, MessageDialog, PopupMenu
- SSH Telnet 终端退出后保持程序继续运行 收藏
- 通过tmux让程序在ssh退出后保持运行
- Activity运行状态以及完全退出程序
- DigitalClock---TextClock---AnalogClock---Chronometer---程序退出保持后台运行设置
- SSH Telnet 终端退出后保持程序继续运行
- Android 锁屏状态下如何保持程序正常运行
- SSH Telnet 终端退出后保持程序继续运行
- 如何在电脑睡眠状态下保持程序运行
- 通过tmux让程序在xshell退出后保持运行
- Android开发之程序运行时保持屏幕、CPU、键盘灯的状态
- Flash builder程序运行时全屏模式运行,而且用户按ESC键时,也不退出全屏模式。
- 程序偶尔在进程退出的时候崩溃。
- 运行java小程序引起IE崩溃一例
- 监控程序运行状态
- symbian 隐藏程序图标和 运行状态
- MSN2009登陆时程序崩溃自动退出的解决方法。
- 如何使PC上运行的程序不能退出,并且用户只能使用当前正在运行的程序。不能使用PC上的其他程序