ここにあるアプリに追加のシグナル ハンドラーを追加しようとしていたところ、作成者がsigaction()
他のシグナル ハンドラーをセットアップしていたことに気付きました。使うつもりでしたsignal()
。慣例に従うには使用する必要がありますsigaction()
が、最初から書いている場合はどれを選択すればよいですか?
9 に答える
sigaction()
やむを得ない理由がない限り使用してください。
このsignal()
インターフェースは古さ (したがって可用性) が高く、C 標準で定義されています。sigaction()
それにもかかわらず、それを回避する多くの望ましくない特性があります-明示的に追加されたフラグを使用して、古い動作sigaction()
を忠実にシミュレートできるようにしない限り。signal()
- この
signal()
関数は、現在のハンドラーの実行中に他のシグナルが到着するのを (必ずしも) ブロックしません。sigaction()
現在のハンドラーが戻るまで、他のシグナルをブロックできます。 - この
signal()
関数は (通常)SIG_DFL
ほとんどすべてのシグナルのシグナル アクションを (デフォルト) にリセットします。これは、signal()
ハンドラーが最初のアクションとして自身を再インストールする必要があることを意味します。また、シグナルが検出されてからハンドラーが再インストールされるまでの間に脆弱性のウィンドウが開かれ、その間にシグナルの 2 番目のインスタンスが到着すると、デフォルトの動作 (通常は終了し、場合によっては偏見を伴う - 別名コア ダンプ) が発生します。 - の正確な動作は
signal()
システムによって異なります。標準では、これらのバリエーションが許可されています。
sigaction()
これらは、一般に の代わりに使用する正当な理由ですsignal()
。ただし、のインターフェイスsigaction()
は間違いなくより扱いにくいです。
sighold()
どちらを使用する場合でも、 、
sigignore()
、
sigpause()
など
の代替信号インターフェイスに惑わされないでください
sigrelse()
。それらは名目上は の代替sigaction()
ですが、ほとんど標準化されておらず、本格的な使用ではなく下位互換性のために POSIX に存在しています。POSIX 標準では、マルチスレッド プログラムでの動作は未定義であると規定されていることに注意してください。
マルチスレッドのプログラムとシグナルは、まったく別の複雑な話です。 私の知る限り、マルチスレッドアプリケーションでは両方とも問題signal()
ありません。sigaction()
Cornstalks は次のように述べ ています。
Linuxのmanページに
signal()
は次のように書かれています:
signal()
マルチスレッド プロセスでの の影響は規定されていません。
sigaction()
したがって、マルチスレッド プロセスで安全に使用できるのはこれだけだと思います。
それは面白い。この場合、Linux のマニュアル ページは POSIX よりも制限が厳しいです。POSIX は次のように指定しますsignal()
。
プロセスがマルチスレッドである場合、またはプロセスがシングルスレッドであり、次の結果以外でシグナル ハンドラーが実行される場合:
abort()
、raise()
、kill()
、pthread_kill()
、またはを呼び出して、sigqueue()
ブロックされていないシグナルを生成するプロセス- 保留中のシグナルがブロック解除され、ブロック解除した呼び出しが返される前に配信される
errno
として宣言されたオブジェクトに値を代入する以外に、シグナルハンドラが静的ストレージ期間以外のオブジェクトを参照するvolatile sig_atomic_t
場合、またはシグナルハンドラがこの標準で定義されている関数のうちの 1 つ以外の関数を呼び出す場合、動作は未定義です。信号の概念。
signal()
したがって、POSIXは、マルチスレッド アプリケーションでの の動作を明確に指定しています。
それにもかかわらず、sigaction()
本質的にすべての状況で優先されるべきであり、移植可能なマルチスレッドコードはsigaction()
、圧倒的な理由がない限り使用する必要があります (「標準 C で定義された関数のみを使用する」など)。そうです、C11 コードはマルチ-スレッド)。これは基本的に、この回答の冒頭の段落にも書かれていることです。
要するに:
sigaction()
は適切で明確に定義されていますが、Linux 関数であるため、Linux でのみ機能します。signal()
は良くなく、定義も不十分ですが、C 標準関数であるため、何に対しても機能します。
Linux の man ページには、それについて何と書かれていますか?
man 2 signal
(ここでオンラインで参照してください)は次のように述べています。
signal() の動作は UNIX のバージョンによって異なり、歴史的に Linux のバージョンによっても異なります。その使用を避けてください:
sigaction(2)
代わりに使用してください。以下の移植性を参照してください。
移植性 signal() の唯一の移植可能な使用法は、シグナルの性質を SIG_DFL または SIG_IGN に設定することです。signal() を使用してシグナルハンドラを確立するときのセマンティクスは、システムによって異なります (POSIX.1 では、このバリエーションが明示的に許可されています)。この目的には使用しないでください。
つまり、使用しないでくださいsignal()
。代わりsigaction()
に使おう!
GCCは何を考えていますか?
互換性に関する注意: について前述したように
signal
、この関数は可能な限り使用しないでください。sigaction
が推奨される方法です。
ソース: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
したがって、Linux と GCC の両方signal()
が を使用sigaction()
せず、代わりに使用すると言う場合、疑問が生じます: この紛らわしいsigaction()
ものを一体どのように使用するのでしょうか!?
使用例:
ここでGCC の優れsignal()
た例を読んでください: 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()
:
1. 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 https://stackoverflow.com/questions/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);
//...
}
2.signal()
上記のようにシグナル ハンドラをアタッチするのは適切な方法ではありませんが、for の使用方法を知っておくとよいでしょう。
これは、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);
…
}
注意すべき主なリンク:
- 標準シグナル: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
- 終了シグナル: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
signal()
公式の GCC使用例を含む基本的なシグナル処理: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling- 公式の GCC
sigaction()
使用例: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html sigemptyset()
とを含む信号セットsigfillset()
。私はまだこれらを正確に理解していませんが、重要であることは知っています: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html
以下も参照してください。
- 「実行中のプロセスにシグナルを手動で送信する方法」および「プログラムでシグナルをトラップする方法」(例:Bash)に関する私の回答。
- TutorialsPoint C++ シグナル処理 [優れたデモ コード付き]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
- https://www.tutorialspoint.com/c_standard_library/signal_h.htm
私にとって、この下の行は決定するのに十分でした:
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(3)
マニュアルページから:
説明
This signal() facility is a simplified interface to the more general sigaction(2) facility.
どちらも同じ基本機能を呼び出します。おそらく両方で単一の信号の応答を操作するべきではありませんが、それらを混合しても何も壊れてはいけません...
少なくとも理論的には、移植性が高いため、signal() を使用します。POSIX 互換レイヤーを持たず、signal() をサポートする最新のシステムを思い付くことができるコメント投稿者に投票します。
GLIBC ドキュメントからの引用:
1 つのプログラム内で signal 関数と sigaction 関数の両方を使用することは可能ですが、少し奇妙な方法で相互作用する可能性があるため注意が必要です。
sigaction 関数は signal 関数よりも多くの情報を指定するため、signal からの戻り値は sigaction の可能性の全範囲を表すことはできません。したがって、signal を使用してアクションを保存し、後で再確立すると、sigaction で確立されたハンドラーを適切に再確立できない場合があります。
結果として問題が発生するのを避けるために、プログラムで sigaction を使用する場合は、常に sigaction を使用してハンドラーを保存および復元してください。sigaction はより一般的であるため、最初にシグナルまたは sigaction で確立されたかどうかに関係なく、任意のアクションを適切に保存および再確立できます。
システムによっては、signal でアクションを確立してから sigaction で調べると、取得するハンドラ アドレスが signal で指定したものと異なる場合があります。シグナルのアクション引数として使用するのに適していない場合もあります。ただし、sigaction の引数として使用することはできます。この問題は、GNU システムでは決して発生しません。
したがって、1 つのプログラム内で一貫していずれかのメカニズムを使用する方がよいでしょう。
移植性に関する注意: 基本的なシグナル関数は ISO C の機能ですが、sigaction は POSIX.1 標準の一部です。非 POSIX システムへの移植性が心配な場合は、代わりに signal 関数を使用する必要があります。
Copyright (C) 1996-2008 Free Software Foundation, Inc.
この文書は、GNU Free Documentation License、バージョン 1.2、または Free Software Foundation によって発行されたそれ以降のバージョンの条件に基づいて、コピー、配布、および/または変更する許可が与えられています。インバリアント セクションなし、フロント カバー テキストなし、バック カバー テキストなし。ライセンスのコピーは、「GNU Free Documentation License」というタイトルのセクションに含まれています。
マンページからsignal(7)
プロセス向けシグナルは、現在シグナルがブロックされていないスレッドのいずれかに配信される可能性があります。複数のスレッドでシグナルのブロックが解除されている場合、カーネルはシグナルを配信する任意のスレッドを選択します。
そして、この「問題」はsignal (2)とsigaction(2 ) に存在すると思います。そのため、シグナルと pthread には注意してください。
...そしてsignal(2)は、glibcを使用するLinuxでは、その下でsigaction(2)を呼び出すようです。