【问题标题】:Self restart program on segfault under LinuxLinux下segfault上的自重启程序
【发布时间】:2011-04-11 19:41:06
【问题描述】:

在 Linux 下,程序通过在崩溃处理程序中捕获异常(例如在 segfault 上)在崩溃时重新启动自己的最佳方式是什么?

【问题讨论】:

标签: c linux restart segmentation-fault recover


【解决方案1】:

最简单的是

while [ 1 ]; do ./program && break; done

基本上,你运行程序直到它返回 0,然后你就中断了。

【讨论】:

  • 这个解决方案和llasram's 可以使故意终止进程变得困难(至少用户最知道发生了什么......)。根据预期用途,这可能是好是坏。
  • @dmc 当然,最简单的不一定好。如果不了解更多有关需求,就很难说。我喜欢你的回答,因为它更健壮。
  • 哦,我没有抱怨。这具有简单的优点,有时您希望无知的用户将其杀死...
  • @dmckee - llasram's answer 具有父级可以捕获SIGTERM,向子级发出信号,等待,然后有序退出的属性。这个答案应该可以适应做同样的事情——bash 的作业控制应该足够丰富以支持它。
  • @dmckee 我在重启之前添加了一个sleep 2,这样第二个 Ctrl+C 就会停止无限循环。
【解决方案2】:

SIGSEGV 可以被捕获(参见man 3 signalman 2 sigaction),并且程序可以在其自身上调用exec 系列函数之一以重新启动。对于大多数运行时崩溃也是如此(SIGFPESIGILLSIGBUSSIGSYS、...)。

不过,在这样做之前我会考虑一下。对于 unix 程序来说,这是一种相当不寻常的策略,您可能会让您的用户感到惊讶(也不一定以令人愉快的方式)。

无论如何,如果有你想在死前清理的任何资源,请确保不要SIGTERM自动重启,否则愤怒的用户会使用SIGKILL,你会留下一个烂摊子。

【讨论】:

  • 不是一个好主意,来自信号手册页:“根据 POSIX,进程的行为在它忽略不是由 kill(2) 生成的 SIGFPE、SIGILL 或 SIGSEGV 信号后未定义或raise(3)。”
  • @Paul:我以前没有注意到这一点。我不清楚的事情是运行一个处理程序,该处理程序在您复制 argv[0] 的某个静态变量上调用 exec 构成“忽略”信号。我的直觉是声称它没有。无论如何,我已经能够在 Mac OS 和 linux 上可靠地处理 SIGSEGV。我不记得处理过 SIGFPE,而且我认为我没有每个生成的 SIGILL 或 SIGBUS。当然,这里的其他建议很好,可以实现 OP 的愿望,但我从字面上理解了标题。
【解决方案3】:

你可以有一个循环,在其中你基本上是fork(),在子进程中做真正的工作,然后等待子进程并检查它在父进程中的退出状态。您还可以使用以类似方式监视和重新启动程序的系统,例如 daemontools、runit 等。

【讨论】:

    【解决方案4】:

    作为对这里提议的补充:

    另一个选择是像 getty 守护进程那样做。请参阅 /etc/inittab 和相应的 inittab(5) 手册页。它似乎是最全系统的平均值 ;-)。

    它可能看起来像下面的文件片段。明显的优势,这种方法是相当标准的,它允许通过运行级别控制你的守护进程。

    # Run gettys in standard runlevels
    1:2345:respawn:/sbin/mingetty tty1
    2:2345:respawn:/sbin/mingetty tty2
    3:2345:respawn:/sbin/mingetty tty3
    4:2345:respawn:/sbin/mingetty tty4
    5:2345:respawn:/sbin/mingetty tty5
    6:2345:respawn:/sbin/mingetty tty6
    

    【讨论】:

    • 这是正确的做法。
    【解决方案5】:

    进程无法自行重新启动,但您可以使用crontab(1) 之类的实用程序来安排脚本以定期检查进程是否仍处于活动状态。

    【讨论】:

    • 没有什么能阻止程序在argv[0] 上调用exec(几乎总是它自己的可执行文件)...
    • “崩溃”是操作系统发送信号(默认行为“终止进程”)。默认行为可以替换为用户定义的函数...
    • 如果内存严重损坏以至于您在 SEGV 处理程序中获得了第二个 SEGV,该怎么办?从本质上讲,从另一个进程操作更可靠。
    • 我同意@dmckee,AFAIK 调用 exec 基本上是另一个具有相同 PID 的进程。它重生,所有内存都丢失了(不确定共享内存和类似资源是否有任何问题需要显式清理)[只要你复制 argv[0] 的内存是干净的,你应该是好的]跨度>
    • @dmckee 如何替换默认行为?可以从程序内部完成吗?我的意思是,程序是否可以捕获自身崩溃然后重新启动。
    【解决方案6】:

    程序本身显然不应该检查它是否正在运行:)

    大多数企业解决方案实际上只是从ps() 为给定字符串提取输出的奇特方式,并在满足某些条件时执行操作 - 即如果找不到您的流程,则调用启动脚本.

    【讨论】:

      【解决方案7】:

      如果它特定于段错误,请尝试以下代码。这可以根据需要进行修改。

      #include <stdio.h> 
      #include <signal.h> 
      #include <setjmp.h> 
      #include <poll.h>
      
      sigjmp_buf buf; 
      void handler(int sig) { 
      siglongjmp(buf, 1); 
      } 
      int main() { 
      //signal(SIGINT, handler); 
      //register all signals
      struct sigaction new_action, old_action;
      new_action.sa_handler = handler;
      sigemptyset (&new_action.sa_mask);
      new_action.sa_flags = 0;
      
      sigaction (SIGSEGV, NULL, &old_action);
      if (old_action.sa_handler != SIG_IGN)
      sigaction (SIGSEGV, &new_action, NULL);
      
      if (!sigsetjmp(buf, 1)){
      printf("starting\n"); 
      //code or function/method here
      }
      else{  
      printf("restarting\n"); 
       //code or function/method here
      }
      while(1) {
      poll(NULL,0,100); //ideally use usleep or nanosleep. for now using poll() as a timer
      printf("processing...\n");
      }
      return 0; //or exit(SUCESS)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-08
        • 2011-03-18
        • 2014-04-17
        • 2022-07-14
        • 1970-01-01
        • 2017-06-01
        • 2012-08-26
        • 2012-03-14
        相关资源
        最近更新 更多