0

信号とフォークで宿題をして、信号に問題があります。

関数を作成しました:

void trata_sinal_int() {
    char op[2];

    printf("\nTerminate? (y/n)\n");

    scanf("%s", op);

    if (op[0] == 'y') {
        printf("Bye Bye\n");
        exit(0);
    }

}

そして主に私は持っています:

signal(SIGINT, trata_sinal_int);

これを実行して押すとCTRL ^C、関数void trata_sinal_int()が呼び出され、メッセージが表示されます。

プログラムを押すyと期待どおりに終了しますが、nプログラムを押すとプログラムは終了します。を押す前の状態には戻っていませんCTRL ^C

これは起こるはずですか?

4

2 に答える 2

5

準拠している標準によって異なりますが、標準Cでは、タイプの変数を変更したり、シグナルハンドラーから(または)をvolatile sig_atomic_t呼び出したりする以上のことはできません。POSIXははるかに寛大です。シグナルハンドラーのコードは、ユーザーインタラクションでいっぱいで、POSIXでさえ許可されている限界を超えています。通常、シグナルハンドラー関数は小さくて洗練されたものにする必要があります。_Exitabort()signal()

シグナルハンドラ関数は次のようになっていることに注意してください。

void trata_sinal_int(int signum)
{

これにより、型の不一致に関するキャストやコンパイラの警告なしでコンパイルできます。このsignal()関数は、呼び出されたときにシグナルハンドラをデフォルトの動作にリセットする場合があります。古典的には、シグナルハンドラー内でシグナルハンドラーを復元する必要があります。

    signal(signum, trata_sinal_int);

これまでのところ、それはすべてかなり一般的で半些細なことです。

を入力するControl-Cと、システムは信号が最初に受信されたときとほぼ同じ場所に戻ります。ただし、次に何が起こるかは、それがどこにあったかによって異なります(ハンドラー内で非常に注意する必要がある理由の1つ)。たとえば、内部のフリーリストポインタを操作している最中でmalloc()あれば、そこに戻りますがmalloc()、ハンドラー内で再度呼び出すと、すべての地獄が解き放たれる可能性があります。システムコールの中にいる場合は、通話が中断されるか(エラー表示とで戻るerrno == EINTR)、または中断したところから再開される場合があります。それ以外の場合は、計算が実行されていた場所に戻る必要があります。


これが、テストリグに組み込まれたコード(の修正バージョン)です。関数はpause()、信号を待ってから戻ります。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

static void trata_sinal_int(int signum)
{
    char op[2];

    signal(signum, trata_sinal_int);

    printf("\nTerminate? (y/n)\n");
    scanf("%s", op);

    if (op[0] == 'y')
    {
        printf("Bye Bye\n");
        exit(0);
    }
}

int main(void)
{
    signal(SIGINT, trata_sinal_int);
    for (int i = 0; i < 3; i++)
    {
        printf("Pausing\n");
        pause();
        printf("Continuing\n");
    }
    printf("Exiting\n");
    return(0);
}

scanf()私は本当に安全ではないことを指摘する必要があります。サイズ2のバッファは、バッファオーバーフローへのオープンな招待です。また、システムコールのエラーチェックも行っていません。

BSD派生のMacOSX10.7.5でテストしました。signal()BSDはずっと前に「信頼できる信号」を導入したため(POSIX以前)、このプラットフォームではのリセットが不要になる可能性があります。


ISO / IEC 9899:2011§7.14.1.1signal機能

abort¶5または関数を呼び出した結果以外の結果としてシグナルが発生したraise場合、シグナルハンドラーが、値を割り当てる以外にロックフリーのアトミックオブジェクトではない静的またはスレッドの保存期間を持つオブジェクトを参照すると、動作は定義されません。として宣言されたオブジェクトに対して、またはシグナルハンドラーが、関数、関数、 関数、または関数の呼び出しを引き起こしたシグナルに対応するシグナル番号に等しい最初の引数を持つ関数volatile sig_atomic_t以外の標準ライブラリ内の関数を呼び出すハンドラ。さらに、そのような関数の呼び出しが戻り値になる場合、errnoの値は不確定です。252)abort_Exitquick_exitsignalsignalSIG_ERR

252)非同期シグナルハンドラーによってシグナルが生成された場合、動作は定義されていません。

への参照quick_exit()はC2011で新しく追加されました。それらはC1999には存在しませんでした。

POSIX 2008

シグナルの概念に関するセクションでは、POSIXのシグナルハンドラー内で許可されていることと許可されていないことについて、かなり詳細に説明しています。

于 2012-10-13T15:35:47.267 に答える
2

まず、シグナル ハンドラーは完全に非同期シグナル セーフではありません。main() は基本的にシグナルを待機している間は何もしていないと想定しているため、実際にはこれはおそらく問題にはなりません。しかし、とにかくそれは間違いなく正しくありません。

プログラムが終了する理由については、printf、sscanf などの FILE* 関数の無効な使用が原因で、シグナル ハンドラーで segfault:s をカウントしないため、シグナルが受信されたときに実行中のシステム コール (またはほとんどのシステム コール) が発生します。と中断されEAGAINます。

メインのようなものを使用sleep()してシグナルが発生するのを待機している場合、シグナルは中断されます。手動で再起動する必要があります。

これを回避するには、おそらく、より移植性の高いsigactionインターフェイスをsignal. これにより、システム コールを再起動することを示すことができます。

that FILE *関数 (および や などのグローバル状態を使用する他のほとんどの関数malloc)がシグナル ハンドラーで許可されていない理由freeは、シグナルが到着したときに、同じ状態で別の操作を行っている可能性があるためです。

これにより、セグメンテーション違反やその他の未定義の操作が発生する可能性があります。

これを実装するための通常の「トリック」は、セルフパイプを持つことです。シグナルハンドラーはパイプにバイトを書き込み、メインループはこれを(通常は待機pollまたは同様の方法で)確認し、それに基づいて動作します。

シグナルハンドラーでユーザーとの対話を絶対に行いたい場合は、関数ではなくwrite()andを使用する必要があります。read()FILE*

于 2012-10-13T15:34:46.393 に答える