シグナルハンドラーでfree()またはclose()を実行できますか?[複製]

Dec 10 2020

私は次のようなコードを持っています:

//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+Csignal_handler関数を1回実行するように割り込みを変更しました。のようないくつかの関数freeは非同期セーフではなく、では実行されない可能性があることをどこかで読みましたsignal_handlerが、それについてはよくわかりません。

私のような機能を実行することができfreecloseexitあるいはpthread_joinシグナルハンドラでは?

回答

1 dratenik Dec 10 2020 at 15:46

いいえ。シグナルハンドラー内で安全に呼び出すことができるのは、man 7signal-safetyにリストされている関数のみです。

closeリストされており、安全である必要があります。freeではありません。あなたがそのソースコードを見る必要がある理由のために(それはロックを含んでいます)。exit任意のクリーンアップハンドラーを呼び出すことができるため、安全ではありません。_exitクリーンアップせずに突然終了するものがあります。

1 MarcoBonelli Dec 10 2020 at 15:46

技術的には、シグナルハンドラーでそのような関数を呼び出すプログラムをコンパイルできますが、それを妨げるものは何もありません。ただし、実行しようとしている関数が非同期シグナルセーフでない場合は、未定義の動作が発生します。安全でない関数があなたが言うように単に「実行されない」というわけではありません、彼らは非常にうまくいく可能性がありますが、それでも未定義の振る舞いです。

非同期シグナルセーフ機能のリストは、に記載されていman 7 signal-safetyます。このclose()機能は安全ですが、そうfree()phtread_join()はありません。このexit()関数は、シグナルハンドラーから呼び出すことも安全ではありません。そのようなコンテキストを終了する場合は、_exit()代わりにを使用して呼び出す必要があります。

シグナルを受信したときに非同期シグナルセーフではない関数を安全に呼び出す唯一の方法は、呼び出す必要があることを「覚えて」(たとえば、グローバル変数を設定する)、シグナルハンドラから戻ったに呼び出すことです。

1 JohnBode Dec 10 2020 at 16:30

短い答えはノーです:

7.1.4ライブラリ関数の使用
...
4標準ライブラリの関数は、再入可能であることが保証されておらず、静的またはスレッドの保存期間でオブジェクトを変更する場合があります。 188)
188)したがって、シグナルハンドラは、一般に、標準ライブラリ関数を呼び出すことはできません。
C2011オンラインドラフト

結果の実際の例-Accessデータベースと通信するシステムで作業しました。を使用してコンソールにエラーメッセージを書き込もうとしたシグナルハンドラーがありましたが、シグナルfprintf処理プロセス中にstderr、データベースを格納する.mdbファイルにマップされ、ヘッダーが上書きされ、修復できないほどデータベースが破壊されました。

正直なところ、他の場所でチェックするフラグを設定する以外に、シグナルハンドラーでできることはたくさんありません。

BasileStarynkevitch Dec 10 2020 at 16:24

シグナルハンドラーで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)も参照してください。