23

Linux でさまざまなシグナルに対して多数のシグナル ハンドラを登録したアプリケーションを作成しました。プロセスがシグナルを受信した後、制御は登録したシグナルハンドラーに転送されます。このシグナル ハンドラで、必要な作業をいくつか行います。次に、デフォルトのシグナル ハンドラ ieSIF_DFLまたはを呼び出したいと思いますSIG_IGN。ただし、SIG_DFLSIG_INGは両方とも、それぞれ数値 0 と 1 に展開されるマクロであり、無効な関数アドレスです。

デフォルトのアクションを呼び出す方法はありますSIG_DFLSIG_IGN?

SIG_DFLまたはの効果を達成するために SIG_ING、それぞれ exit(1) を呼び出して何もしません。しかし、次のような信号についてSIGSEGVは、コア ダンプも必要です。一般に、オペレーティング システムが行うように、デフォルトの動作を と同じにし、SIG_DFL動作を無視したいと思います。SIG_IGN

4

4 に答える 4

14

GNU C Library Reference Manualには、シグナル処理に関するすべてを説明する章全体があります。

独自のハンドラーをインストールすると、以前に設定したシグナル ハンドラー (関数ポインター) が常に取得されます (signal()またはのマンページを参照sigaction())。

previous_handler = signal(SIGINT, myhandler);

一般的なルールは、いつでも前のハンドラーとraise()シグナルにリセットできるということです。

void myhandler(int sig) {
  /* own stuff .. */
  signal(sig, previous_handler);
  raise(sig);
  /* when it returns here .. set our signal handler again */
  signal(sig, myhandler);
}

一般的な規則には 1 つの欠点があります。シグナルにマップされるハードウェア例外は、通常、例外の原因となった特定の命令に割り当てられます。そのため、シグナルを再度発生させると、関連付けられた命令は元の命令と同じではありません。これは、他のシグナルハンドラに害を及ぼす可能性がありますが、そうすべきではありません。

もう 1 つの欠点は、発生した各シグナルによって多くの処理時間が発生することです。の過度の使用を防ぐためにraise()、次の代替手段を使用できます。

  1. SIG_DFL関数ポインターがアドレスを指している場合0(これは明らかに有効なアドレスではありません)。したがって、ハンドラーとシグナルを再度リセットする必要があります。raise()

    if (previous_handler == SIG_DFL)
    {
      signal(sig, SIG_DFL);
      raise(sig);
      signal(sig, myhandler);
    } 
  2. SIG_IGN値があります1(これも無効なアドレスです)。ここでは、そのまま戻ることができます (何もしないでください)。

    else if (previous_handler == SIG_IGN)
    {
      return;
    } 
  3. それ以外の場合 (どちらSIG_IGNでもないSIG_DFL)、有効な関数ポインターを受け取っていないため、ハンドラーを直接呼び出すことができます。

    else
    {
      previous_handler(sig);
    }

もちろん、さまざまな API も考慮する必要があります (signal()およびのマンページを参照してくださいsigaction())。

于 2012-11-08T13:43:28.493 に答える
12

前のハンドラーを保存して、適切なタイミングで呼び出すことができます。

ハンドラーをインストールします。古いハンドラーを必ず保存してください

static struct sigaction new_sa, old_sa;

new_sa.sa_handler = my_handler;
sigemptyset(&new_handler.sa_mask);

if (sigaction(signo, &new_sa, &old_sa) == -1) {
    /* handle sigaction error */
}

新しいハンドラーで、古いハンドラーを呼び出します

(*old_sa.sa_handler)(signo)

もう一度上げる必要も、面倒なことをする必要もありません。古いハンドラーを呼び出すだけです(もちろん、保存したsigactionので、古い性質にアクセスできますなど)。

于 2011-05-16T10:17:18.310 に答える
7

通常のアプローチは、シグナルハンドラーをリセットしてからraise()、シグナルを再度リセットすることです。

SIGINTハンドラーの例を次に示します。

void sigint_handler(int num)
{
    /* handle SIGINT */

    // call default handler
    signal(SIGINT, SIG_DFL);
    raise(SIGINT);
}
于 2011-05-16T10:17:00.437 に答える
2

シグナルハンドラーがカーネルに実装されているとすると、私が見る唯一の方法は

  • ハンドラーをリセットし、
  • raise()再び信号
于 2011-05-16T10:06:42.433 に答える