回答:
使用する sigaction()
やむを得ない理由がない限り。
signal()
インタフェースは、その有利な古(したがって、可用性)を有し、それはC標準で定義されています。それにもかかわらず、それはsigaction()
回避する多くの望ましくない特性を持っています-明示的に追加されたフラグを使用してsigaction()
、古いsignal()
動作を忠実にシミュレートできるようにしない限り。
signal()
関数は、現在のハンドラーの実行中に他のシグナルの到着を(必要に応じて)ブロックしません。sigaction()
現在のハンドラが戻るまで他のシグナルをブロックできます。signal()
関数は、(通常)SIG_DFL
ほとんどすべての信号の信号アクションを(デフォルト)にリセットします。つまり、signal()
ハンドラーは最初のアクションとして自身を再インストールする必要があります。また、シグナルが検出されてからハンドラーが再インストールされるまでの間に脆弱性のウィンドウが開き、その間にシグナルの2番目のインスタンスが到着すると、デフォルトの動作(通常は終了し、場合によっては偏見-コアダンプ)が発生します。signal()
システム間で異なります—標準ではそれらのばらつきを許可しています。これらは、のsigaction()
代わりに使用する一般的な理由ですsignal()
。ただし、のインターフェイスsigaction()
は紛れもなく厄介です。
あなたが使用する2つのどちらのような代替信号インタフェースに誘惑されていない
sighold()
、
sigignore()
、
sigpause()
と
sigrelse()
。これらは名目上はの代替ですがsigaction()
、ほとんど標準化されておらず、深刻な使用ではなく下位互換性のためにPOSIXに存在しています。POSIX標準では、マルチスレッドプログラムでの動作は定義されていないことに注意してください。
マルチスレッドのプログラムとシグナルは、まったく別の複雑な話です。 AFAIK、両方ともマルチスレッドアプリケーションで問題signal()
ありsigaction()
ません。
Linuxのmanページは
signal()
言う:
signal()
マルチスレッドプロセスでの影響は不定です。したがって、
sigaction()
マルチスレッドプロセスで安全に使用できるのはこの方法だけだと思います。
それは面白い。この場合、LinuxのマニュアルページはPOSIXよりも制限的です。POSIXは以下を指定しsignal()
ます:
プロセスがマルチスレッドである場合、またはプロセスがシングルスレッドであり、シグナルハンドラーが以下の結果以外で実行される場合:
- プロセスが呼び出し
abort()
、raise()
、kill()
、pthread_kill()
、またはsigqueue()
ブロックされていない信号を生成します- 保留中のシグナルがブロック解除され、ブロック解除した呼び出しが戻る前に配信されます
errno
として宣言されたオブジェクトに値を割り当てること以外の静的ストレージ期間以外のオブジェクトをシグナルハンドラーが参照するvolatile sig_atomic_t
場合、またはシグナルハンドラーがこの標準で定義されている関数以外の関数を呼び出す場合、動作は未定義です。信号の概念。
そのため、POSIX signal()
はマルチスレッドアプリケーションにおけるの動作を明確に規定しています。
それでも、sigaction()
は本質的にすべての状況で推奨されます。そしてsigaction()
、それができない圧倒的な理由がない限り、ポータブルマルチスレッドコードを使用する必要があります(「標準Cによって定義された関数のみを使用する」など)。そして、はい、C11コードはマルチにすることができます-スレッド)。これは基本的に、この回答の最初の段落でも述べられていることです。
SA_RESETHAND
、またSA_NODEFER
。
sigaction()
、基本的にの標準C仕様を使用する義務がありますsignal()
。しかし、それはあなたができることのためにあなたに非常に貧弱なオプションのセットを与えます。次のことができますvolatile sig_atomic_t
。「クイック終了」関数の1つを呼び出す(_Exit()
、quick_exit()
)またはabort()
; signal()
現在のシグナル番号をシグナル引数として呼び出します。戻ります。以上です。それ以外のものは、移植可能であることが保証されていません。これは非常に厳格なため、ほとんどの人はこれらのルールを無視しますが、結果のコードは危険です。
sigaction()
GCC自身による優れたデモ:gnu.org/software/libc/manual/html_node/… ; そしてsignal()
GCC自身からの優れたデモ:gnu.org/software/libc/manual/html_node/…。signal
デモでSIG_IGN
以前意図的に設定されていた場合は、ハンドラーを無視()から変更しないように注意してください。
私にとって、この下の行は決定するのに十分でした:
sigaction()関数は、信号を制御するためのより包括的で信頼性の高いメカニズムを提供します。新しいアプリケーションは、signal()ではなくsigaction()を使用する必要があります
http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07
ゼロから始める場合でも、古いプログラムを変更する場合でも、sigactionは正しいオプションです。
これらは、OSの信号機能の異なるインターフェースです。signal()は実装で定義された(多くの場合、競合しやすい)動作をし、Windows、OS X、Linuxおよびその他のUNIXシステムで異なる動作をするため、可能であればsigactionを使用してシグナルを送信することをお勧めします。
詳細については、このセキュリティノートを参照してください。
signal()は標準Cですが、sigaction()はそうではありません。
どちらかを使用できる場合(つまり、POSIXシステムを使用している場合)、sigaction()を使用します。signal()がハンドラーをリセットするかどうかは指定されていません。つまり、移植可能にするには、ハンドラー内でもう一度signal()を呼び出す必要があります。さらに悪いのは、競合が発生していることです。2つのシグナルが連続して取得され、ハンドラーを再インストールする前に2番目のシグナルが配信された場合、デフォルトのアクションが実行され、おそらくプロセスを強制終了することになります。 一方、sigaction()は、「信頼できる」シグナルセマンティクスを使用することが保証されています。ハンドラーはリセットされないため、再インストールする必要はありません。SA_RESTARTを使用すると、一部のシステムコールを自動的に再起動することもできます(EINTRを手動で確認する必要がないため)。 sigaction() より多くのオプションがあり、信頼できるため、その使用をお勧めします。
Psst ...言ったことは誰にも言わないでください。しかし、POSIXには現在、signal()のように機能するBSDセマンティクスを提供する関数bsd_signal()があり、信頼性があります。その主な用途は、信頼できる信号を想定した古いアプリケーションを移植することであり、POSIXはその使用を推奨していません。
sigaction()
よく定義されていますが、Linuxの機能なので、Linuxでのみ機能します。signal()
悪いし、定義も不十分ですが、C標準関数なので、何でも機能します。
man 2 signal
(ここでオンラインで参照してください)状態:
signal()の動作はUNIXのバージョンによって異なり、Linuxのバージョンによって歴史的にも異なります。その使用は避けてください
sigaction(2)
。代わりに使用してください。以下の移植性を参照してください。移植性signal()の唯一の移植可能な使用は、シグナルの後処理をSIG_DFLまたはSIG_IGNに設定することです。signal()を使用してシグナルハンドラーを確立するときのセマンティクスはシステムによって異なります(POSIX.1ではこのバリエーションを明示的に許可しています)。この目的には使用しないでください。
つまり、を使用しないでくださいsignal()
。sigaction()
代わりに使用してください!
互換性に関する注意:上記のとおり
signal
、この関数は可能な限り回避する必要があります。sigaction
推奨される方法です。
出典:https : //www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
したがって、LinuxとGCCの両方が使用しないと言った場合 signal()
がを使用せsigaction()
ず、代わりに使用に、それは疑問を投げかけます。一体、この混乱しているsigaction()
ものをどのように使用するのでしょうか!?
signal()
ここでGCCの優れた例を読んでください:ください https //www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
そして、sigaction()
ここで彼らの優れた例: https //www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
それらのページを読んだ後、私は次のテクニックを思いつきました sigaction()
:
sigaction()
上記のように、シグナルハンドラーをアタッチする正しい方法であるため、#include <errno.h> // errno
#include <signal.h> // sigaction()
#include <stdio.h> // printf()
#include <string.h> // strerror()
#define LOG_LOCATION __FILE__, __LINE__, __func__ // Format: const char *, unsigned int, const char *
#define LOG_FORMAT_STR "file: %s, line: %u, func: %s: "
/// @brief Callback function to handle termination signals, such as Ctrl + C
/// @param[in] signal Signal number of the signal being handled by this callback function
/// @return None
static void termination_handler(const int signal)
{
switch (signal)
{
case SIGINT:
printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal);
break;
case SIGTERM:
printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n", signal);
break;
case SIGHUP:
printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal);
break;
default:
printf("\nUnk signal (%i) caught.\n", signal);
break;
}
// DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc.
exit(signal);
}
/// @brief Set a new signal handler action for a given signal
/// @details Only update the signals with our custom handler if they are NOT set to "signal ignore" (`SIG_IGN`),
/// which means they are currently intentionally ignored. GCC recommends this "because non-job-control
/// shells often ignore certain signals when starting children, and it is important for children
/// to respect this." See
/// https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
/// and https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html.
/// Note that termination signals can be found here:
/// https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
/// @param[in] signal Signal to set to this action
/// @param[in] action Pointer to sigaction struct, including the callback function inside it, to attach to this signal
/// @return None
static inline void set_sigaction(int signal, const struct sigaction *action)
{
struct sigaction old_action;
// check current signal handler action to see if it's set to SIGNAL IGNORE
sigaction(signal, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
{
// set new signal handler action to what we want
int ret_code = sigaction(signal, action, NULL);
if (ret_code == -1)
{
printf(LOG_FORMAT_STR "sigaction failed when setting signal to %i;\n"
" errno = %i: %s\n", LOG_LOCATION, signal, errno, strerror(errno));
}
}
}
int main(int argc, char *argv[])
{
//...
// Register callbacks to handle kill signals; prefer the Linux function `sigaction()` over the C function
// `signal()`: "It is better to use sigaction if it is available since the results are much more reliable."
// Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
// and /programming/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711.
// See here for official gcc `sigaction()` demo, which this code is modeled after:
// https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
// Set up the structure to specify the new action, per GCC's demo.
struct sigaction new_action;
new_action.sa_handler = termination_handler; // set callback function
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
// SIGINT: ie: Ctrl + C kill signal
set_sigaction(SIGINT, &new_action);
// SIGTERM: termination signal--the default generated by `kill` and `killall`
set_sigaction(SIGTERM, &new_action);
// SIGHUP: "hang-up" signal due to lost connection
set_sigaction(SIGHUP, &new_action);
//...
}
signal()
上記のように、シグナルハンドラーをアタッチするのに適した方法ではありませんが、それを使用する方法を知ることは依然として良いです。これは、GCCのデモンストレーションコードをコピーして貼り付けたものです。
#include <signal.h>
void
termination_handler (int signum)
{
struct temp_file *p;
for (p = temp_file_list; p; p = p->next)
unlink (p->name);
}
int
main (void)
{
…
if (signal (SIGINT, termination_handler) == SIG_IGN)
signal (SIGINT, SIG_IGN);
if (signal (SIGHUP, termination_handler) == SIG_IGN)
signal (SIGHUP, SIG_IGN);
if (signal (SIGTERM, termination_handler) == SIG_IGN)
signal (SIGTERM, SIG_IGN);
…
}
signal()
使用例を含む基本的な信号処理:https : //www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handlingsigaction()
使用例:https : //www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.htmlsigemptyset()
およびsigfillset()
; 私はまだこれらを正確に理解していませんが、重要であることを知っています:https : //www.gnu.org/software/libc/manual/html_node/Signal-Sets.htmlsignal(3)
manページから:
説明
This signal() facility is a simplified interface to the more general sigaction(2) facility.
どちらも同じ基本機能を呼び出します。おそらく、単一の信号の応答を両方で操作してはいけませんが、それらを混合しても問題は発生しません...
signal()よりもsigaction()を使用することをお勧めします。もう1つポイントを追加したいと思います。sigaction()は、停止したプロセスのpidなどのより多くのオプションを提供します(siginfo_t構造体を使用して可能)。
少なくとも理論的には、移植性が高いため、signal()を使用します。私は、POSIX互換性レイヤーがなく、signal()をサポートする最新のシステムを思い付くことができるコメント投稿者に投票します。
GLIBCドキュメントからの引用:
単一のプログラム内でシグナル関数とシグアクション関数の両方を使用することは可能ですが、これらはわずかに奇妙な方法で相互作用する可能性があるため、注意する必要があります。
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」というタイトルのセクションに含まれています。
manページのsignal(7)から
プロセス向けのシグナルは、現在シグナルがブロックされていないスレッドのいずれかに配信されます。複数のスレッドでシグナルのブロックが解除されている場合、カーネルはシグナルを配信する任意のスレッドを選択します。
そして、この「問題」はsignal(2)とsigaction(2)に存在すると言えます。したがって、シグナルとpthreadに注意してください。
...そして 、信号(2)を呼び出すように思わは、sigaction(2)のglibcでのLinuxで下を。
signal
は、実際にはUnix System Vの動作に関するものです。POSIXはこの動作またははるかに健全なBSD動作のいずれかを許可しますが、どちらを取得するかがわからないため、それでも使用するのが最善sigaction
です。