12

ポインター演算からセグメンテーション違反を起こすプログラムがあります。これが発生することはわかっていますが、セグメンテーション違反かどうかを事前に簡単に確認することはできません。入力データを「事前スキャン」して、セグメンテーション違反が発生するかどうかを確認することもできます (これを判断することはできません)。または、非常に大量の作業が必要になるポインター演算を使用しないように修正するか、セグメンテーション違反をキャッチしようとすることができます。だから私の質問:

1) C でセグメンテーション違反をキャッチするにはどうすればよいですか? OSの何かがセグメンテーション違反を引き起こすことは知っていますが、セグメンテーション違反が発生した場合、Cプログラムは何ができるのSegmentation faultでしょうか?

2) これはどの程度移植性がありますか?

これは非常に移植性の低い動作だと思うので、セグメンテーション違反をキャッチするコードを投稿する場合は、それがどのように機能するか教えてください。私は Mac OS X を使用していますが、自分のプログラムができるだけ多くのプラットフォームで動作するようにしたいと考えており、オプションが何であるかを確認したいと考えています。

そして心配しないでください - 基本的に私がやりたいことは、よりユーザーフレンドリーなエラーメッセージを出力し、malloc()ed メモリをいくらか解放してから死ぬことだけです。発生したすべてのセグメンテーション違反を無視して先に進むつもりはありません。

4

8 に答える 8

23

SIGSEGV はトラップ可能であり、これは POSIX であるため、その意味で移植可能です。

セグメンテーション違反の原因となっている問題を修正するのではなく、セグメンテーション違反を処理したいように思われるのではないかと心配しています。OS に問題があるのか​​、それとも自分のコードに問題があるのか​​を選択する必要がある場合、どちらを選択するかはわかっています。そのバグを探し出して修正し、テスト ケースを作成して、そのバグに二度と悩まされないようにすることをお勧めします。

于 2009-02-16T18:45:22.957 に答える
17

関数signalを使用して、シグナルの新しいシグナルハンドラーをインストールできます。

   #include <signal.h>
   void (*signal(int signum, void (*sighandler)(int)))(int);

次のコードのようなもの:

signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault
signal(SIGTERM , clean_exit_on_sig);

void 
clean_exit_on_sig(int sig_num)
{
        printf ("\n Signal %d received",sig_num);
}
于 2009-02-16T18:49:59.060 に答える
10

シグナルハンドラを定義する必要があります。これは、関数 を使用して Unix システムで実行されますsigaction。Fedora 64 ビットと 32 ビット、および Sun Solaris で同じコードを使用してこれを実行しました。

于 2009-02-16T18:44:30.400 に答える
5

シグナル ハンドラでの安全なアクションは非常に限られています。再入可能であることがわかっていないライブラリ関数を呼び出すことは安全ではありません。これにより、たとえばfree()andが除外されprintf()ます。ベスト プラクティスは、変数を設定して返すことですが、これはあまり役に立ちません。などのシステムコールを使用しても安全write()です。

ここに示す 2 つのバックトレースの例backtrace_symbols_fd()では、生の fd を直接使用しているため関数は安全ですが、 への呼び出しfprintf()は正しくないため、 の使用に置き換える必要があることに注意してくださいwrite()

于 2009-02-16T19:17:51.850 に答える
1

SIGSEGVハンドラーを提供する必要があります。これはかなりまともなように見えます。

于 2009-02-16T18:51:44.033 に答える
1

glibcのbacktrace()を使用してSIGSEGVをキャッチし、スタックトレースを出力する方法の例を次に示します。

C++アプリがクラッシュしたときにスタックトレースを生成する方法

これを使用してセグメンテーション違反をキャッチしてクリーンアップできますが、注意が必要です。特に、malloc()のような呼び出しを行う場合は、シグナルハンドラーで多くのことを実行しないでください。シグナルセーフではない呼び出しがたくさんあり、たとえば、malloc内からmallocに電話をかけると、足を撃ってしまう可能性があります。

于 2009-02-16T18:56:45.473 に答える
1

信号処理は、UNIXマシン(これにはmacとlinuxが含まれます)間で(比較的)移植可能です。大きな違いは、シグナル処理ルーチンに引数として渡される例外の詳細にあります。残念ですが、より合理的なエラーメッセージ(障害が発生した場所や原因など)を出力する場合は、おそらくそのために多数の#ifdefが必要になります...

さて、ここにあなたが始めるためのコードフラグメントがあります:

#include <signal.h>

/* reached when a segv occurrs */
void
SEGVFunction( SIGARGS )
{
     ...
}

...
main(...) {
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */
    ...
    ... do your work ...
}

あなたの仕事は:

  • SIGARGSが何であるかを確認します(OSに依存するため、ifdefを使用します)
  • sigArgsの例外情報からfault-addressとpcを抽出する方法を参照してください
  • 合理的なメッセージを印刷する
  • 出口

理論的には、シグナルハンドラーでPCにパッチを適用して(障害が発生した命令の後で)、続行することもできます。ただし、一般的なシグナルハンドラーは、exit()またはlongjmp()を使用して、メインの保存場所に戻ります。

よろしく

于 2009-02-16T19:00:07.087 に答える
0

存在しない問題を解決しようとしていると思います。少なくとも、あなたは間違った目的で取り組んでいます。このエラー/例外は OS によってスローされるため、セグメンテーション違反をキャッチすることはできません(プログラムが原因であり、OS はそれをキャッチするだけです)。

入力に関する戦略を再考することをお勧めします:なぜそれをサニタイズできないのですか? 最も重要なのはサイズのチェックです。このため、C stdlib には適切な機能があります。もちろん、コンテンツに関する有効な入力を確認する必要があります。はい、これはおそらく多くの作業を必要とするでしょうが、堅牢なプログラムを作成する唯一の方法です。

編集:私は C の専門家ではなく、セグメンテーション違反でさえシグナル ハンドラで処理できることを知りませんでした。それでも、上記の理由から、正しい方法ではないと思います。

于 2009-02-16T18:48:44.837 に答える