シグナルハンドラーでfree()またはclose()を実行できますか?[複製]
私は次のようなコードを持っています:
//global variables
void signal_handler() {
//deallocation of global variables
free(foo);
close(foo_2);
exit(0);
}
int main () {
signal(SIGINT, signal_handler);
//irrelevant code
}
ご覧のとおり、プロセスをすぐに強制終了するのではなくCTRL+C
、signal_handler
関数を1回実行するように割り込みを変更しました。のようないくつかの関数free
は非同期セーフではなく、では実行されない可能性があることをどこかで読みましたsignal_handler
が、それについてはよくわかりません。
私のような機能を実行することができfree
、close
、exit
あるいはpthread_join
シグナルハンドラでは?
回答
いいえ。シグナルハンドラー内で安全に呼び出すことができるのは、man 7signal-safetyにリストされている関数のみです。
close
リストされており、安全である必要があります。free
ではありません。あなたがそのソースコードを見る必要がある理由のために(それはロックを含んでいます)。exit
任意のクリーンアップハンドラーを呼び出すことができるため、安全ではありません。_exit
クリーンアップせずに突然終了するものがあります。
技術的には、シグナルハンドラーでそのような関数を呼び出すプログラムをコンパイルできますが、それを妨げるものは何もありません。ただし、実行しようとしている関数が非同期シグナルセーフでない場合は、未定義の動作が発生します。安全でない関数があなたが言うように単に「実行されない」というわけではありません、彼らは非常にうまくいく可能性がありますが、それでも未定義の振る舞いです。
非同期シグナルセーフ機能のリストは、に記載されていman 7 signal-safetyます。このclose()
機能は安全ですが、そうfree()
でphtread_join()
はありません。このexit()
関数は、シグナルハンドラーから呼び出すことも安全ではありません。そのようなコンテキストを終了する場合は、_exit()代わりにを使用して呼び出す必要があります。
シグナルを受信したときに非同期シグナルセーフではない関数を安全に呼び出す唯一の方法は、呼び出す必要があることを「覚えて」(たとえば、グローバル変数を設定する)、シグナルハンドラから戻った後に呼び出すことです。
短い答えはノーです:
7.1.4ライブラリ関数の使用C2011オンラインドラフト
...
4標準ライブラリの関数は、再入可能であることが保証されておらず、静的またはスレッドの保存期間でオブジェクトを変更する場合があります。 188)
188)したがって、シグナルハンドラは、一般に、標準ライブラリ関数を呼び出すことはできません。
結果の実際の例-Accessデータベースと通信するシステムで作業しました。を使用してコンソールにエラーメッセージを書き込もうとしたシグナルハンドラーがありましたが、シグナルfprintf
処理プロセス中にstderr
、データベースを格納する.mdbファイルにマップされ、ヘッダーが上書きされ、修復できないほどデータベースが破壊されました。
正直なところ、他の場所でチェックするフラグを設定する以外に、シグナルハンドラーでできることはたくさんありません。
シグナルハンドラーでfree()またはclose()を実行できますか?
あなたは絶対にすべきではありません。signal(7)およびsignal-safety(7)を参照してください。
実際には、おそらく半分以上の時間で必要なように機能する可能性があります。IIRC、GCCコンパイラはあなたがやりたいようにやっていて、通常は動作します。
より良いアプローチは、(シグナルハンドラーの内部から)パイプ(7 )へのwrite(2)を使用し、poll(2)または関連するものでそのパイプ(メインプログラム内)を時々チェックすることです。
または、シグナルハンドラーにいくつかvolatile sigatomic_t flag;
(おそらくそれもあるはずです)を設定し_Atomic、そのフラグを他の場所(メインプログラム内、シグナルハンドラーの外)でチェックすることもできます。
Qtは、私が数分でできるよりも優れていると説明しています。
Linuxでは、signalfd(2)およびeventfd(2)も参照してください。