Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在分割破坏后恢复生命_C_Exception Handling_Signals_Segmentation Fault - Fatal编程技术网

C 在分割破坏后恢复生命

C 在分割破坏后恢复生命,c,exception-handling,signals,segmentation-fault,C,Exception Handling,Signals,Segmentation Fault,分段错误发生后,是否可以恢复C程序的正常执行流 struct A { int x; }; A* a = 0; a->x = 123; // this is where segmentation violation occurs // after handling the error I want to get back here: printf("normal execution"); // the rest of my source code.... 我想要一种类似于Jav

分段错误发生后,是否可以恢复C程序的正常执行流

struct A {
    int x;
};
A* a = 0;

a->x = 123; // this is where segmentation violation occurs

// after handling the error I want to get back here:
printf("normal execution");
// the rest of my source code....
我想要一种类似于Java、C#等中存在的NullPointerException的机制

<>强>注<强>:请不要告诉我C++中有异常处理机制,因为我知道,不要告诉我在分配之前应该检查每个指针等等。
我真正想要实现的是回到正常的执行流程,如上面的示例所示。我知道可以使用POSIX信号执行一些操作。它应该是什么样子?其他想法?

您可以使用信号处理器捕获分段错误,并决定继续执行程序(风险自负)

信号名称为
SIGSEGV

您必须从
signal.h
标题使用
sigaction()
函数

基本上,它的工作方式如下:

struct sigaction sa1;
struct sigaction sa2;

sa1.sa_handler = your_handler_func;
sa1.sa_flags   = 0;
sigemptyset( &sa1.sa_mask );

sigaction( SIGSEGV, &sa1, &sa2 );
以下是处理函数的原型:

void your_handler_func( int id );

正如你所见,你不需要回来。程序将继续执行,除非您决定自己从处理程序中停止它。

调用此函数,当发生SEGVULT时,您的代码将执行segv_处理程序,然后继续返回到原来的位置

void segv_handler(int)
{
  // Do what you want here
}

signal(SIGSEGV, segv_handler);

在POSIX中,当您这样做时,您的进程将被发送到SIGSEGV。默认处理程序只是使程序崩溃。您可以使用signal()调用添加自己的处理程序。你可以通过自己处理信号来实现你喜欢的任何行为。

“所有事情都是允许的,但并非所有事情都是有益的”-通常一个segfault游戏结束是有充分理由的。。。一个更好的办法是保持数据的持久性(数据库,或者至少是文件系统),并使其能够以这种方式恢复原来的位置。这将为您提供更好的数据可靠性。

本手册为您提供了如何编写信号处理程序的清晰图像

A signal handler is just a function that you compile together with the rest
of the program. Instead of directly invoking the function, you use signal 
or sigaction to tell the operating system to call it when a signal arrives.
This is known as establishing the handler.
在您的情况下,您必须等待SIGSEGV指示分段故障。可以找到其他信号的列表

信号处理器大致分为两类

  • 您可以让handler函数注意到信号是通过调整一些 全局数据结构,然后正常返回
  • 您可以让handler函数终止程序或传输 控制到可以从引起信号的情况中恢复的程度
    SIGSEGV
    属于程序错误信号

    \include
    
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <ucontext.h>
    
    void safe_func(void)
    {
        puts("Safe now ?");
        exit(0); //can't return to main, it's where the segfault occured.
    }
    
    void
    handler (int cause, siginfo_t * info, void *uap)
    {
      //For test. Never ever call stdio functions in a signal handler otherwise*/
      printf ("SIGSEGV raised at address %p\n", info->si_addr);
      ucontext_t *context = uap;
      /*On my particular system, compiled with gcc -O2, the offending instruction
      generated for "*f = 16;" is 6 bytes. Lets try to set the instruction
      pointer to the next instruction (general register 14 is EIP, on linux x86) */
      context->uc_mcontext.gregs[14] += 6; 
      //alternativly, try to jump to a "safe place"
      //context->uc_mcontext.gregs[14] = (unsigned int)safe_func;
    }
    
    int
    main (int argc, char *argv[])
    {
      struct sigaction sa;
      sa.sa_sigaction = handler;
      int *f = NULL;
      sigemptyset (&sa.sa_mask);
      sa.sa_flags = SA_SIGINFO;
      if (sigaction (SIGSEGV, &sa, 0)) {
          perror ("sigaction");
          exit(1);
      }
      //cause a segfault
      *f = 16; 
      puts("Still Alive");
      return 0;
    }
    
    $ ./a.out
    SIGSEGV raised at address (nil)
    Still Alive
    
    #包括 #包括 #包括 #包括 #包括 #包括 无效安全函数(无效) { 现在安全了吗; 退出(0);//无法返回main,这是发生segfault的地方。 } 无效的 处理程序(int原因、siginfo\u t*info、void*uap) { //永远不要在信号处理程序中调用stdio函数*/ printf(“在地址%p\n处引发的SIGSEGV”,信息->si_地址); ucontext\u t*context=uap; /*在我使用gcc-O2编译的特定系统上,有问题的指令 为“*f=16;”生成的是6个字节。让我们尝试设置指令 指向下一条指令的指针(通用寄存器14是EIP,在linux x86上)*/ context->uc_mcontext.gregs[14]+=6; //或者,试着跳到一个“安全的地方” //context->uc_mcontext.gregs[14]=(unsigned int)safe_func; } int main(int argc,char*argv[]) { struct-sigaction-sa; sa.sa_sigaction=handler; int*f=NULL; sigemptyset(和sa.sa_面具); sa.sa_flags=sa_SIGINFO; if(sigaction(SIGSEGV和sa,0)){ 佩罗尔(“sigaction”); 出口(1); } //引起故障 *f=16; 看跌期权(“仍然活着”); 返回0; } 美元/年 SIGSEGV在地址处引发(无) 还活着

    如果我在生产代码中看到这样的东西,我会用球棒打别人,这是一个丑陋的,好玩的黑客。您将不知道SEGFULT是否损坏了您的一些数据,您将没有明智的恢复方法,并且知道现在一切正常,没有可移植的方法来进行恢复。唯一稍微明智的做法是尝试记录一个错误(直接使用write(),而不是任何stdio函数-它们不是信号安全的),然后可能重新启动程序。对于这些情况,最好编写一个superwisor进程来监视子进程的退出,记录它并启动一个新的子进程。

    请参阅R.对MacMake答案的评论

    扩展他所说的(在处理SIGSEV,或者,在这种情况下,SIGFPE之后,CPU+OS可以将您返回到有问题的insn)下面是一个关于零除处理的测试:

    #include <stdio.h>
    #include <limits.h>
    #include <string.h>
    #include <signal.h>
    #include <setjmp.h>
    
    static jmp_buf  context;
    
    static void sig_handler(int signo)
    {
        /* XXX: don't do this, not reentrant */
        printf("Got SIGFPE\n");
    
        /* avoid infinite loop */
        longjmp(context, 1);
    }
    
    int main()
    {
        int a;
        struct sigaction sa;
    
        memset(&sa, 0, sizeof(struct sigaction));
        sa.sa_handler = sig_handler;
        sa.sa_flags = SA_RESTART;
        sigaction(SIGFPE, &sa, NULL);
    
        if (setjmp(context)) {
                /* If this one was on setjmp's block,
                 * it would need to be volatile, to
                 * make sure the compiler reloads it.
                 */
                sigset_t ss;
    
                /* Make sure to unblock SIGFPE, according to POSIX it
                 * gets blocked when calling its signal handler.
                 * sigsetjmp()/siglongjmp would make this unnecessary.
                 */
                sigemptyset(&ss);
                sigaddset(&ss, SIGFPE);
                sigprocmask(SIG_UNBLOCK, &ss, NULL);
    
                goto skip;
        }
    
        a = 10 / 0;
    skip:
        printf("Exiting\n");
    
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    静态jmp_buf上下文;
    静态无效信号处理器(int signo)
    {
    /*XXX:不要这样做,不要重入*/
    printf(“Got SIGFPE\n”);
    /*避免无限循环*/
    longjmp(上下文,1);
    }
    int main()
    {
    INTA;
    struct-sigaction-sa;
    memset(&sa,0,sizeof(struct-sigaction));
    sa.sa_handler=sig_handler;
    sa.sa_标志=sa_重启;
    sigaction(SIGFPE和sa,空);
    if(setjmp(上下文)){
    /*如果这个在setjmp的街区上,
    *它必须是不稳定的
    *确保编译器重新加载它。
    */
    sigset_t ss;
    /*根据POSIX it,确保解锁SIGFPE
    *调用其信号处理程序时被阻止。
    *sigsetjmp()/siglongjmp会使这变得不必要。
    */
    SIG&ss;
    sigaddset(&ss,SIGFPE);
    sigprocmask(SIG_解除阻塞,&ss,空);
    跳槽;
    }
    a=10/0;
    跳过:
    printf(“退出”);
    返回0;
    }
    
    不,在任何逻辑意义上,分段故障后都不可能恢复正常执行。您的程序刚刚尝试取消对空指针的引用。如果您的程序预期的内容没有实现,您将如何继续正常工作?这是一个编程错误,唯一安全的方法就是退出

    以som为例
    void foobarMyProcess(struct SomeStruct* structPtr)
    {
        char* aBuffer = structPtr->aBigBufferWithLotsOfSpace; // if structPtr is NULL, will SIGSEGV
        //
        // if you SIGSEGV and come back to here, at this point aBuffer contains whatever garbage was in memory at the point
        // where the stack frame was created
        //
        strcpy(aBuffer, "Some longish string");  // You've just written the string to some random location in your address space
                                                 // good luck with that!
    
    }
    
    #include <iostream>
    #include <signal.h>
    #include <setjmp.h>
    static jmp_buf buf;
    int counter = 0;
    void signal_handler(int)
    {
         longjmp(buf,0);
    }
    int main()
    {
        signal(SIGSEGV,signal_handler);
        setjmp(buf);
        if(counter++ == 0){ // if we did'nt try before
        *(int*)(0x1215) = 10;  // access an other process's memory
        }
        std::cout<<"i am alive !!"<<std::endl; // we will get into here in any case
        system("pause");
     return 0;   
    }