多くのスレッドを作成し、組み込みコンピューターの電源がシャットダウンされるか、ユーザーがkill
を使用するまで実行するプログラムがあります。 ctrlc プロセスを終了します。
次にコードとmain()の外観を示します。
_static int terminate = 0; // does this need to be volatile?
static void sighandler(int signum) { terminate = 1; }
int main() {
signal(SIGINT, sighandler);
// ...
// create objects, spawn threads + allocate dynamic memory
// ...
while (!terminate) sleep(2);
// ...
// clean up memory, close threads, etc.
// ...
signal(SIGINT, SIG_DFL); // is this necessary?
}
_
私はいくつかのことを考えています:
信号処理は必要ですか?
私はこのスレッドを読みました "Linux Cが正常な終了のためにkillシグナルをキャッチしている" 、OSがクリーンアップを処理するようです。したがって、シグナルハンドラを無限ループに置き換えて、OSがスレッドを正常に終了したり、メモリの割り当てを解除したりすることはできますか?
クリーンターミネーションに関して考慮する必要がある他の信号はありますか?このスレッド "SIGINTは他の終了シグナルとどのように関連していますか?" は、関係する可能性のあるすべてのシグナルをリストするのに役立ちましたが、実際にいくつの処理が必要ですか?
私の例の終了変数は揮発性である必要がありますか?私は、この変数が揮発性である多くの例とそうでないものを見てきました。
私はsignal()
が非推奨になり、sigaction()
を使用することを読みました。以前のsignal()
呼び出しから変換する方法を示す本当に良い例はありますか?作成/パスする必要のある新しい構造と、それがどのように組み合わされるかについて問題があります。
signal()
への2回目の呼び出しは必要ですか?sigaction()
について気にする必要のある同様のものはありますか?
明確にするために、my:main loopをどちらかまで実行するために達成しようとしているすべてのこと ctrlc または、電源が切断されているか、何か本当に悪いことが起こっています。
[Q-3]この例の
terminate
変数はvolatile
である必要がありますか?私は、この変数が揮発性である多くの例とそうでないものを見てきました。
フラグterminate
は_volatile sig_atomic_t
_である必要があります:
ハンドラ関数は非同期で呼び出すことができるからです。つまり、ハンドラーはプログラムの任意の時点で予期せず呼び出される可能性があります。 2つのシグナルが非常に短い間隔で到着した場合、1つのハンドラーが別のハンドラー内で実行できます。また、_volatile sig_atomic_t
_を宣言することをお勧めします。この型は常にアトミックにアクセスされ、変数へのアクセスの中断に関する不確実性を回避します。 volatile
は、コンパイラーに最適化せずにレジスターに入れるように指示します。 (read: Atomic Data Access and Signal Handling for detail expiation))。
もう1つ参照: 24.4.7原子データアクセスと信号処理 。さらに、7.14.1.1-5のC11標準は、_volatile sig_atomic_t
_のオブジェクトのみがシグナルハンドラーからアクセスできることを示しています(他のオブジェクトへのアクセスには未定義の動作があります)。
[Q-4]
signal()
は非推奨になり、sigaction()
を使用するようになりました。以前のsignal()
呼び出しから変換する方法を示す本当に良い例はありますか?作成/パスする必要のある新しい構造と、それがどのように組み合わされるかについて問題があります。
以下の例(およびコメント内のリンク)が役立ちます。
_// 1. Prepare struct
struct sigaction sa;
sa.sa_handler = sighandler;
// 2. To restart functions if interrupted by handler (as handlers called asynchronously)
sa.sa_flags = SA_RESTART;
// 3. Set zero
sigemptyset(&sa.sa_mask);
/* 3b.
// uncomment if you wants to block
// some signals while one is executing.
sigaddset( &sa.sa_mask, SIGINT );
*/
// 4. Register signals
sigaction( SIGINT, &sa, NULL );
_
参照:
sigaction()
で正確に説明されています。[Q-5]
signal()
への2回目の呼び出しは必要ですか?sigaction()
について気にする必要のある同様のものはありますか?
プログラムの終了前にdefault-actionに設定する理由は、私には不明確です。次の段落で答えが得られると思います。
信号の処理
シグナルの呼び出しは、シグナルのoneの発生のみに対するシグナル処理を確立します。シグナル処理関数が呼び出される前に、ライブラリはシグナルをリセットし、同じシグナルが再び発生した場合にデフォルトのアクションが実行されるようにします。シグナル処理をリセットすると、たとえば、シグナルハンドラーで実行されたアクションによって同じシグナルが再度発生した場合に、無限ループを防ぐことができます。発生するたびにハンドラーをシグナルに使用する場合は、ハンドラー内でsignalを呼び出して、シグナルを復元する必要があります。信号処理の回復には注意が必要です。たとえば、
SIGINT
の処理を継続的に再開すると、プログラムを中断および終了する機能が失われる可能性があります。
signal()
関数は、次の受信信号のみのハンドラーを定義しますその後、デフォルトのハンドラーが復元されます。したがって、プログラムがデフォルト以外のハンドラーを使用してシグナルの処理を継続する必要がある場合は、シグナルハンドラーがsignal()
を呼び出す必要があります。
詳細については、ディスカッションをお読みください: シグナルハンドラーを再度有効にする場合 。
[Q-1a]信号処理は必要ですか?
はい、Linuxがクリーンアップを行います。たとえば、ファイルまたはソケットを閉じない場合、Linuxはプロセスの終了後にクリーンアップを実行します。ただし、Linuxはクリーンアップをすぐに実行する必要がない場合があり、時間がかかる場合があります(システムのパフォーマンスを高く保つために、またはその他の問題が発生する可能性があります)。たとえば、tcp-socketを閉じずにプログラムが終了した場合、カーネルはソケットをすぐに閉じず、すべてのデータが送信されたことを確認します。TCPは、可能であれば配信を保証します。
[Q-1b]したがって、シグナルハンドラを無限ループに置き換えて、OSがスレッドを正常に終了できるようにして、メモリなど?
いいえ、オペレーティングシステムはプログラムの終了後にのみクリーンアップを実行します。プロセスの実行中、そのプロセスに割り当てられているリソースはOSによって要求されません。 (OSは、プロセスが無限ループにあるかどうかを認識できません- これは解決できない問題です )。プロセスの終了後にOSがクリーンアップ操作を実行するようにしたい場合は、シグナルを処理する必要はありません(プロセスがシグナルによって異常終了した場合でも)。
[Q]my:main loopを実行するために達成しようとしているすべて ctrlc または、電源が切断されているか、何か本当に悪いことが起こっています。
いいえ、制限があります!すべての信号をキャッチすることはできません。一部の信号はキャッチできません。 SIGKILL
とSIGSTOP
は両方とも終了信号です。引用:
—マクロ:int
SIGKILL
SIGKILL
信号は、プログラムを即座に終了させるために使用されます。これは処理または無視できないため、常に致命的です。また、notこの信号をブロックすることができます。
だから、あなたは作ることはできません 中断できないプログラム(中断されないプログラム) !
確かではありませんが、WindowsシステムでTSR(ある種のカーネルモードフッキング)を作成することで、このようなことができるのではないでしょうか。論文の時間から、一部のウイルスはタスクマネージャーからも終了できないことを覚えていますが、管理者権限でユーザーをだますことも信じています。
この回答がお役に立てば幸いです。
代わりにsigaction
を使用するには、次のような関数を使用できます。
/*
* New implementation of signal(2), using sigaction(2).
* Taken from the book ``Advanced Programming in the UNIX Environment''
* (first edition) by W. Richard Stevens.
*/
sighandler_t my_signal(int signo, sighandler_t func)
{
struct sigaction nact, oact;
nact.sa_handler = func;
nact.sa_flags = 0;
# ifdef SA_INTERRUPT
nact.sa_flags |= SA_INTERRUPT;
# endif
sigemptyset(&nact.sa_mask);
if (sigaction(signo, &nact, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
1。シグナル処理は必要ですか?
2。クリーンターミネーションに関して注意する必要がある他の信号はありますか?
最初に、彼のページを見てください: The GNU Library Signals 終了信号はあなたが世話するものです。ただし、デバッグの目的を除いて、ソフトウェアでそれらを見つけられない場合でも、SIGUSR1とSIGUSR2を確認してください。
ソフトを突然終了させたくない場合は、このすべての終了信号を処理する必要があります。
3。私の例の終了変数は揮発性である必要がありますか?
4。私は
signal()
が非推奨になり、sigaction()
を使用することを読みました
Sigaction()
はPOSIXですが、シグナルはC標準です。
Signal()
は私にとってはうまく機能しますが、例が必要な場合は: IBMの例
これには信号を使用する必要はありません。標準の終了は、キャッチされる必要はありません。キャッチする理由はあるかもしれませんが、それはO/Sが必要とするものではなく、アプリケーションにかかっています。
シグナルに関しては、最近ではシグナルではなくsigactionを使用する必要があります。これは標準化の問題を回避します。
信号ハンドラーは、再入可能であるように作成する必要があります。これは、終了変数が揮発性である必要はありませんが、それをどのように使用するかによって異なります。
W.リチャードスティーブンスの本 "UNIX環境での高度なプログラミング" は、信号を処理する理由と方法の良い例です。
そして、いいえ、アプリケーションが終了する前にデフォルトのハンドラーを戻す必要はありません。ハンドラーはアプリケーションに対してのみ有効であるため、アプリを強制終了するだけの場合は必要ありません。
まず第一に-信号を処理する必要があるかどうかわからない場合は、おそらく処理しません。シグナルは、ソケットを閉じる、終了する前に他の接続されたプロセスにメッセージを送信する、write()からのSIGPIPEシグナルを処理するなどの特定のケースでの処理が必要です(おそらくそれ以上)。
第二に-このwhile (!terminate) sleep(2);
はあまり良くありません-最悪の場合、ユーザー(またはシステム)が2秒間待機し、プログラムにSIGKILLを送信しなければならないことにイライラすることがあります。
私見ここでの最良の解決策は signalfdとselectを使用 であるため、2秒待つことなくプログラムを終了できます。