web-dev-qa-db-ja.com

セグメンテーション障害処理

セグメンテーションエラーまたはctrl-cをキャッチするために使用するアプリケーションがあります。以下のコードを使用して、セグメンテーションフォールトをキャッチできますが、ハンドラーが何度も呼び出されています。どうすればそれらを止めることができますか。参考までに、アプリケーションを終了したくありません。破損したすべてのバッファを解放するだけで十分です。

出来ますか?

void SignalInit(void )
{

struct sigaction sigIntHandler;

sigIntHandler.sa_handler = mysighandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
sigaction(SIGSEGV, &sigIntHandler, NULL);

}

ハンドラーは次のようになります。

void mysighandler()
{
MyfreeBuffers(); /*related to my applciation*/
}

ここで、セグメンテーションフォールト信号の場合、ハンドラーが複数回呼び出されており、明らかなように、MyfreeBuffers()は既に解放されたメモリを解放するためのエラーを返します。一度だけ解放したいが、それでもアプリケーションを終了したくない。

助けてください。

32
user1225606

SIGSEGVなどのデフォルトのアクションはプロセスを終了することですが、ハンドラーをインストールすると、デフォルトの動作をオーバーライドしてハンドラーを呼び出します。しかし、問題は、ハンドラーが終了した後にセグメンテーション違反の命令が再試行される可能性があり、最初のセグメンテーション違反を修正するための対策を講じていない場合、再試行された命令は再び失敗し、繰り返されます。

したがって、最初にSIGSEGVになった命令を見つけて修正してみてください(ハンドラーでbacktrace()のようなものを呼び出して、何が間違っているかを自分で確認できます)

また、POSIX標準では、

プロセスの動作は、kill()、[RTS] sigqueue()、またはraise()によって生成されなかった[XSI] SIGBUS、SIGFPE、SIGILL、またはSIGSEGVシグナルのシグナルキャッチ関数から正常に戻った後は未定義です。 )。

したがって、最初にセグメンテーション違反を修正することが理想的です。 segfaultのハンドラーは、根本的なエラー状態をバイパスすることを意図していません

したがって、最良の提案は-SIGSEGVをキャッチしないでください。コアダンプします。コアを分析します。無効なメモリ参照を修正してください。

34
Pavan Manjunath

私は声明にまったく同意しません "SIGSEGVをキャッチしないでください"

これは、unexpected状態に対処するのに非常に良い方法です。そして、それは、エラー条件管理を配布するよりも、[〜#〜] null [〜#〜]setjmp/longjmpに関連付けられたシグナル機構を使用したポインター(malloc障害で指定)に対処する方がはるかにクリーンです。すべてのコードに沿って。

ただし、SEGVで '' sigaction ''を使用する場合は、SA_NODEFERsa_flagsと言うことを忘れないでください。または、事実SEGVはハンドラーを一度だけトリガーします。

#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

static void do_segv()
{
  int *segv;

  segv = 0; /* malloc(a_huge_amount); */

  *segv = 1;
}

sigjmp_buf point;

static void handler(int sig, siginfo_t *dont_care, void *dont_care_either)
{
   longjmp(point, 1);
}

int main()
{
  struct sigaction sa;

  memset(&sa, 0, sizeof(sigaction));
  sigemptyset(&sa.sa_mask);

  sa.sa_flags     = SA_NODEFER;
  sa.sa_sigaction = handler;

  sigaction(SIGSEGV, &sa, NULL); /* ignore whether it works or not */ 

  if (setjmp(point) == 0)
   do_segv();

  else
    fprintf(stderr, "rather unexpected error\n");

  return 0;
}
10
Champignac

SIGSEGVが再び起動する場合、明らかな結論は、MyfreeBuffers();の呼び出しがnotで根本的な問題を修正したことです(そしてその関数が本当にfree()割り当てられたメモリ、なぜあなたがそう思うだろうかわかりません)。

大まかに言うと、アクセスできないメモリアドレスにアクセスしようとすると、SIGSEGVが起動します。アプリケーションを終了しない場合は、そのメモリアドレスにアクセスできるようにするか、longjmp()を使用して実行パスを変更する必要があります。

9
caf

_SIG_SEGV_の後に続行しようとしないでください。基本的に、アプリケーションの環境が何らかの形で破損していることを意味します。 nullポインターを逆参照しただけの場合もあれば、プログラムのスタックやヒープまたはポインター変数が破損しているバグが原因の場合もありますが、わかりません。 only安全なことは、プログラムを終了することです。

Control-Cを扱うのは完全に合法です。多くのアプリケーションがそれを行いますが、シグナルハンドラで何をするかを正確に注意する必要があります。再入可能でない関数を呼び出すことはできません。つまり、MyFreeBuffers()がstdlib free()関数を呼び出すと、おそらくあなたは台無しになります。プログラムがmalloc()またはfree()の途中でcontrol-Cを押して、ヒープの割り当てを追跡するために使用するデータ構造を操作する途中である場合、ほぼ確実にシグナルハンドラでmalloc()またはfree()を呼び出すと、ヒープが破損します。

シグナルハンドラでできる唯一の安全なことは、シグナルをキャッチしたことを示すフラグを設定することです。その後、アプリはフラグを定期的にポーリングして、何らかのアクションを実行する必要があるかどうかを判断できます。

6
JeremyP

少なくともLinuxでは、-fnon-call-exceptionsオプションを使用したトリックを使用することで解決できるようです。シグナルを一般的なC++例外に変換し、一般的な方法で処理する機能を提供します。 linux3/gcc46: "-fnon-call-exceptions"、どの信号が命令をトラップしていますか? を見てください。

0
Ivan Uskov

SIG_SEGVからの回復の場合、ループ内のイベントの処理とこれらのイベントの1つがセグメンテーション違反を引き起こす場合は、このイベントをスキップして残りのイベントの処理を続行するだけです。私の目には、SIG_SEGVはJavaのNullPointerExceptionに似ています。はい、これらのいずれかの後、状態は一貫性がなく、不明になりますが、場合によっては状況を処理して続行したいことがあります。たとえば、アルゴ取引では、注文の実行を一時停止し、システム全体をクラッシュさせたり、他のすべての注文を台無しにすることなく、トレーダーが手動で引き継ぐことができます。

0
newlogic

状態変数を設定し、設定されていない場合にのみメモリを解放できます。シグナルハンドラーは毎回呼び出され、そのAFAIKを制御することはできません。

0
g13n