4

なんらかの奇跡によってプログラムでセグメンテーション違反が発生した場合、SIGSEGVをキャッチし、ユーザー(おそらくGUIクライアント)に重大な問題が発生したことを単一のリターンコードで知らせたいと思います。同時に、どの信号がキャッチされたかを示す情報をコマンドラインに表示したいと思います。

今日、シグナルハンドラは次のようになります。

void catchSignal (int reason) {
  std :: cerr << "Caught a signal: " << reason << std::endl;
  exit (1);
}

このスレッドから、シグナルハンドラーから非リエントラント関数を呼び出すのは悪であると読んだので、上記の恐怖の叫び声を聞くことができます。

信号を処理してユーザーに情報を提供するためのポータブルな方法はありますか?

編集:または少なくともPOSIXフレームワーク内で移植可能ですか?

4

4 に答える 4

12

このは、POSIX が async-signal-safe であることを保証し、シグナル ハンドラから呼び出すことができるすべての関数を示しています。

この表の「書き込み」コマンドを使用することにより、次の比較的「醜い」ソリューションで問題が解決することが期待されます。

#include <csignal>

#ifdef _WINDOWS_
#define _exit _Exit
#else
#include <unistd.h>
#endif

#define PRINT_SIGNAL(X) case X: \
          write (STDERR_FILENO, #X ")\n" , sizeof(#X ")\n")-1); \
          break;

void catchSignal (int reason) {
  char s[] = "Caught signal: (";
  write (STDERR_FILENO, s, sizeof(s) - 1);
  switch (reason)
  {
    // These are the handlers that we catch
    PRINT_SIGNAL(SIGUSR1);
    PRINT_SIGNAL(SIGHUP);
    PRINT_SIGNAL(SIGINT);
    PRINT_SIGNAL(SIGQUIT);
    PRINT_SIGNAL(SIGABRT);
    PRINT_SIGNAL(SIGILL);
    PRINT_SIGNAL(SIGFPE);
    PRINT_SIGNAL(SIGBUS);
    PRINT_SIGNAL(SIGSEGV);
    PRINT_SIGNAL(SIGTERM);
  }

  _Exit (1);  // 'exit' is not async-signal-safe
}

編集: Windows でのビルド。

この 1 つのウィンドウをビルドしようとすると、「STDERR_FILENO」が定義されていないように見えます。ただし、ドキュメントから、その値は「2」のようです。

#include <io.h>
#define STDIO_FILENO 2

編集: 'exit' は、シグナル ハンドラからも呼び出されるべきではありません!

fizzerが指摘したように、上記の_Exitの呼び出しは、HUP や TERM などのシグナルに対する大ハンマー アプローチです。理想的には、これらのシグナルがキャッチされたときに、「volatile sig_atomic_t」タイプのフラグを使用して、終了する必要があることをメイン プログラムに通知できます。

以下は、検索で役立つことがわかりました。

  1. Unix シグナルプログラミング入門
  2. 従来のシグナルの拡張
于 2008-09-19T17:32:48.007 に答える
1

FWIW、2はWindowsの標準エラーでもありますが、write()は_write()と呼ばれるため、条件付きコンパイルが必要になります。あなたも欲しいでしょう

#ifdef SIGUSR1 /* or whatever */

C規格で定義されることが保証されていない信号へのすべての参照の周りなど。

また、上記のように、SIGUSR1、SIGHUP、SIGINT、SIGQUIT、およびSIGTERMをこのように処理する必要はありません。

于 2008-09-19T19:35:24.920 に答える
1

リチャード、まだコメントするのに十分なカルマがないので、新しい答えが怖いです。これらは非同期信号です。いつ配信されるかわからないため、一貫性を保つために完了する必要のあるライブラリコードが含まれている可能性があります。したがって、これらのシグナルのシグナルハンドラーは戻る必要があります。exit()を呼び出すと、ライブラリは、atexit()に登録された関数の呼び出しや標準ストリームのクリーンアップなど、main()後にいくつかの作業を実行します。たとえば、信号が標準ライブラリのI / O関数に到着した場合、この処理は失敗する可能性があります。したがって、C90ではexit()を呼び出すことはできません。C99は、stdlib.hに新しい関数_Exit()を提供することで、要件を緩和していることがわかります。_Exit()は、非同期シグナルのハンドラーから安全に呼び出すことができます。

bk1eへ(いくつかの投稿をコメントしてください) SIGSEGVが同期しているという事実は、再入可能になるように設計されていない関数を使用できない理由です。クラッシュした関数がロックを保持していて、シグナルハンドラーによって呼び出された関数が同じロックを取得しようとした場合はどうなりますか?

これは可能性ですが、問題であるのは「SIGSEGVが同期しているという事実」ではありません。ハンドラーから非リエントラント関数を呼び出すことは、次の2つの理由から、非同期シグナルではさらに悪化します。

  • 非同期シグナルハンドラーは、(通常)通常のプログラム実行に戻って再開することを望んでいます。同期シグナルのハンドラーは(一般的に)とにかく終了するので、クラッシュしてもそれほど多くを失うことはありません。
  • 逆の意味では、同期信号がいつ配信されるかを完全に制御できます。これは、欠陥のあるコードを実行するときに発生し、それ以外のときは発生しません。非同期信号が配信されるタイミングは、まったく制御できません。OP自身のI/Oコード自体が欠陥の原因でない限り(たとえば、不正な文字を出力する*)、彼のエラーメッセージは成功する可能性が十分にあります。
于 2008-09-22T11:54:05.360 に答える
0

プログラムを実行し、異常な終了コードをユーザーに報告するランチャープログラムを作成します。

于 2008-09-19T16:39:56.027 に答える