web-dev-qa-db-ja.com

sigactionとsignalの違いは何ですか?

ここにあるアプリに追加のシグナルハンドラーを追加しようとしていましたが、著者がsigaction()を使用して他のシグナルハンドラーをセットアップしたことに気付きました。私はsignal()を使用するつもりでした。慣例に従うためにsigaction()を使用する必要がありますが、ゼロから作成する場合、どちらを選択する必要がありますか?

125
Matthew Smith

sigaction() を使用するのは、そうしない非常に説得力のある理由がない限りです。

signal() インターフェースには、古さ(および可用性)があり、C標準で定義されています。それでも、sigaction()が回避する多くの望ましくない特性があります。sigaction()に明示的に追加されたフラグを使用して、古いsignal()の動作を忠実にシミュレートできる場合を除きます。

  1. signal()関数は、現在のハンドラーの実行中に他の信号の到着を(必ずしも)ブロックしません。 sigaction()は、現在のハンドラが戻るまで他の信号をブロックできます。
  2. signal()関数(通常)は、ほとんどすべての信号の信号アクションをSIG_DFL(デフォルト)にリセットします。これは、signal()ハンドラーが最初のアクションとして自身を再インストールする必要があることを意味します。また、シグナルが検出されてからハンドラーが再インストールされるまでの間に脆弱性のウィンドウが開き、その間にシグナルの2番目のインスタンスが到着すると、デフォルトの動作(通常は終了し、時には先入観-別名コアダンプ)が発生します。
  3. signal()の正確な動作はシステムによって異なります。標準ではこれらのバリエーションが許可されています。

これらは通常、sigaction()の代わりにsignal()を使用する正当な理由です。ただし、sigaction()のインターフェイスは紛れもなく厄介です。

どちらを使用する場合でも、 sighold()sigignore()sigpause() 、および sigrelse()などの代替信号インターフェースに誘惑されないでください 。それらはsigaction()の名目上の代替物ですが、ほとんど標準化されておらず、深刻な使用ではなく後方互換性のためにPOSIXに存在します。 POSIX標準では、マルチスレッドプログラムでの動作は定義されていないことに注意してください。

マルチスレッドのプログラムとシグナルは、まったく別の複雑な話です。 私の知る限り、signal()sigaction()はマルチスレッドアプリケーションでは問題ありません。

トウモロコシの茎観察

signal() のLinux manページには次のように書かれています:

マルチスレッドプロセスでのsignal()の効果は指定されていません。

したがって、マルチスレッドプロセスで安全に使用できるのはsigaction()だけだと思います。

それは面白い。この場合、LinuxのマニュアルページはPOSIXよりも制限が厳しくなります。 POSIXは signal() を指定します:

プロセスがマルチスレッドである場合、またはプロセスがシングルスレッドであり、次の結果として以外にシグナルハンドラが実行される場合

  • ブロックされていない信号を生成するためにabort()raise()kill()pthread_kill()、またはsigqueue()を呼び出すプロセス
  • ブロックが解除され、ブロックを解除した呼び出しが戻る前に配信される保留中の信号

シグナルハンドラがvolatile sig_atomic_tとして宣言されたオブジェクトに値を割り当てる以外の静的ストレージ期間でerrno以外のオブジェクトを参照する場合、またはシグナルハンドラが定義された関数を呼び出す場合、動作は未定義 Signal Concepts にリストされている関数の1つ以外のこの標準では。

したがって、POSIXは、マルチスレッドアプリケーションでのsignal()の動作を明確に指定します。

それにもかかわらず、sigaction()は本質的にすべての状況で好まれます。そして、移植できないマルチスレッドコードは、それができない圧倒的な理由がない限り、sigaction()を使用する必要があります(「標準Cで定義された関数のみを使用する」など)マルチスレッドである)。これは基本的に、この回答の冒頭の段落でも述べています。

150

私にとって、以下の行は決定するのに十分でした:

Sigaction()関数は、信号を制御するためのより包括的で信頼性の高いメカニズムを提供します。新しいアプリケーションでは、signal()ではなくsigaction()を使用する必要があります

http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07

ゼロから始める場合でも、古いプログラムを変更する場合でも、sigactionは正しいオプションです。

6
San

これらは、OSの信号機能用の異なるインターフェイスです。 signal()は実装で定義された(多くの場合、レースを起こしやすい)振る舞いを持ち、Windows、OS X、Linux、および他のUNIXシステムでは異なる動作をするため、可能な場合はsigactionを使用してシグナル送信することをお勧めします.

詳細については、こちらをご覧ください security note .

5
ididak

signal()は標準Cですが、sigaction()はそうではありません。

どちらかを使用できる場合(つまり、POSIXシステムを使用している場合)、sigaction()を使用します。 signal()がハンドラーをリセットするかどうかは指定されていません。つまり、移植性を確保するには、ハンドラー内でsignal()を再度呼び出す必要があります。さらに悪いことに、2つのシグナルが連続して発生し、ハンドラーを再インストールする前に2番目のシグナルが送信された場合、デフォルトのアクションが発生します。これにより、おそらくプロセスが強制終了されます。一方、sigaction()は、「信頼できる」信号セマンティクスを使用することが保証されています。ハンドラーはリセットされないため、再インストールする必要はありません。 SA_RESTARTを使用すると、いくつかのシステムコールを取得して自動的に再起動することもできます(したがって、EINTRを手動で確認する必要はありません)。 sigaction()にはより多くのオプションがあり、信頼性があるため、その使用をお勧めします。

追伸...これは誰にも言わなかったが、POSIXには現在signal()のように振る舞うbsd_signal()関数がありますが、BSDセマンティクスを提供するため、信頼性があります。その主な用途は、信頼性の高い信号を想定した古いアプリケーションの移植であり、POSIXはそれを使用することを推奨していません。

4
Huzzefakhan

signal(3) manページから:

説明

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

両方とも同じ基礎となる機能を呼び出します。おそらく、両方で単一の信号の応答を操作するべきではありませんが、それらを混合しても何も壊れないはずです...

2
dmckee

Signal()ではなくsigaction()を使用することもお勧めします。もう1つポイントを追加したいと思います。 sigaction()は、死んだプロセスのpidなどのオプションを提供します(siginfo_t構造体を使用して可能)。

1

マニュアルページから signal(7)

プロセス指示シグナルは、現在シグナルがブロックされていないスレッドのいずれかに配信されます。複数のスレッドでシグナルのブロックが解除されている場合、カーネルはシグナルを配信する任意のスレッドを選択します。

そして、この「問題」は signal(2)sigaction(2) に存在すると言います。そのため、シグナルとpthreadには注意してください。

...および signal(2) は、glibcを使用するLinuxの下で sigaction(2) を呼び出すようです。

0
robert.berger

少なくとも理論的には、より移植性があるため、signal()を使用します。 POSIX互換性レイヤーを持たず、signal()をサポートする最新のシステムを思い付くことができるコメンターに投票します。

GLIBCドキュメント からの引用:

単一のプログラム内でsignal関数とsigaction関数の両方を使用することは可能ですが、それらはわずかに奇妙な方法で相互作用する可能性があるため、注意する必要があります。

Sigaction関数は、signal関数よりも多くの情報を指定するため、signalからの戻り値は、sigactionの可能性の全範囲を表現できません。したがって、シグナルを使用してアクションを保存し、後で再確立すると、sigactionで確立されたハンドラーを適切に再確立できない場合があります。

結果として問題が発生しないようにするには、プログラムでsigactionを使用している場合は、常にsigactionを使用してハンドラーを保存および復元します。 sigactionはより一般的であるため、元々シグナルまたはsigactionで確立されたかどうかに関係なく、アクションを適切に保存および再確立できます。

一部のシステムでは、シグナルを使用してアクションを確立し、sigactionを使用して検査すると、取得するハンドラーアドレスがシグナルで指定したものと異なる場合があります。シグナルを伴うアクション引数としての使用には適さない場合もあります。ただし、sigactionの引数として使用することはできます。この問題は、GNUシステムでは発生しません。

したがって、1つのプログラム内でいずれかのメカニズムを一貫して使用するほうが適切です。

移植性に関する注意:基本的なシグナル関数はISO Cの機能ですが、sigactionはPOSIX.1標準の一部です。非POSIXシステムへの移植性が心配な場合は、代わりにシグナル関数を使用する必要があります。

Copyright(C)1996-2008 Free Software Foundation、Inc.

GNU Free Documentation License、バージョン1.2またはFree Software Foundationによって発行されたそれ以降のバージョン、不変セクションなしで、前面カバーテキストなし、および背面カバーテキストなしライセンスのコピーは、「GNU Free Documentation License」というタイトルのセクションに含まれています。

0
bmdhacks