2016-05-27 5 views
3

シグナルハンドラが引数を渡さずにシグナル番号をとる方法signalhandler()main()にありますか? main()内の以下のソースコードシグナルハンドラ内で引数なしsignalhandlerとして、信号システムコールの第二引数を渡すが、それは名前sig_numで引数を収集しているsignalhandler定義に来ている間で例えばLInuxシグナルの概念

...

方法実際に可能ですか?

ANSI Cによれば、引数を渡さないと、関数定義は引数を収集しません。

これについてお手伝いしてください。あなたがsignal(2)の引数としてそれを与えたときに

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

void signalhandler(int sig_num) 
{ 
    printf("caught signal number: %d\n", sig_num); 
} 

int main(void) 
{ 
    while(1) 
    { 
    printf("hello world\n"); 
    sleep(1); 
    signal(SIGINT, signalhandler); 
    } 
} 
+3

あなたは 'signal'が' signalhandler'を直接呼び出すのではないことを理解しています。 –

+0

あなたはもっと詳しく説明できますか? – raj

+1

別の言い方をすると、「シグナル」はどういう意味ですか? –

答えて

2

あなたの機能signalhandlerは呼び出されません。プロトタイプは次のとおりです。

typedef void (*sighandler_t)(int); 
sighandler_t signal(int signum, sighandler_t handler); 

したがって、2番目の引数が関数ポインタであることがわかります。あなたの関数signalhandlerは、後で(シグナルが届いたときに)呼び出され、それを呼び出すコードはintという引数を与えます。

このコールバックメカニズムを理解するために、他の関数ポインタの例を見てください。

+0

ありがとう....良い説明.. – raj

2

signal APIは、指定されたシグナルの受信時に呼び出される提供された関数ポインタを登録します。複数の信号に対して同じ関数ポインタを登録することができます。したがって、呼び出されたときに受け取った信号値が関数に渡されます。

Linux(および私が認識しているすべてのオペレーティングシステム)では、シグナルの配信はプロセスの実行とは非同期であり、通知は即時です。 OSはあたかもプログラムをプリエンプトし、プログラムが現在行っている処理の上にシグナルハンドラ関数呼び出しを注入するかのように動作します。 OSはどの信号が送られたのかを認識し、それをパラメータとして関数呼び出しに渡します。

注:プログラムに同期信号を生成するraiseを使用することが可能です。しかし、通常、シグナルハンドラを直接呼び出すのではなく、OSサービスを使用してプロセスに渡します。

デバッガを使用してシグナルハンドラ内でブレークポイントを設定し、適切な信号を送信すると、バックトレースに信号ハンドラがOSによって注入されていることが表示されることがあります。

void signalhandler (int sig) 
{ 
    write(2, "signal!\n", 8); 
} 

void foo (void) 
{ 
    for (;;) {} 
} 

int main (void) 
{ 
    signal(SIGINT, signalhandler); 
    foo(); 
} 

gdbでプログラムを実行している、あなたはsignalコマンドを使用して信号を提供することができます:

は例えば、プログラムを検討してください。

(gdb) signal SIGINT 
Continuing with signal SIGINT. 

Breakpoint 1, signalhandler (sig=2) at s.c:5 
5  write(2, "signal!\n", 8); 
(gdb) bt 
#0 signalhandler (sig=2) at s.c:5 
#1 <signal handler called> 
#2 foo() at s.c:10 
#3 0x08048498 in main() at s.c:16 

注:結果のバックトレースは次のようになりますシグナルハンドラ関数の呼び出しは、通常の関数呼び出しのように扱われるべきではないことを認識することが重要です。 OSはコールを注入するので、以下で説明する制限があります。

シグナルコールは、コード実行の任意の場所で注入することができます。このため、POSIXでは、特定の関数をシグナルハンドラから呼び出すことが安全であることを要求しています。リエントラントになるように設計されていない関数は、シグナルハンドラ関数へのインジェクションコールによって中断された場合に矛盾した状態になるリスクを負います。

たとえば、リンクリストからノードを削除するなど、データ構造を操作するコードを記述しているとします。しかし、要素のポインタがシグナルが配信されたときに完全に修正されていない場合、シグナルハンドラは破損したリンクリストを見ることがあります。この状況は、特にヒープ割り当てを必要とする関数を呼び出す場合に想定されるよりも頻繁に発生します。

このように、シグナルハンドラを単純なものにするのは最も安全です。たとえば、シグナルハンドラは単にフラグを設定するだけで、アプリケーションコードはフラグが設定されているかどうかを検出するコードを必要とします。

1

私は簡単に説明します。

シグナルは、プログラムにメッセージを配信する方法です。たとえば、プログラムが端末ウィンドウで実行されていて、Ctrl+Cを押すと、端末ウィンドウはSIGINTをプログラムに送信します。

ここで、各プロセスについて、カーネルは、各信号から、信号が受信されるときに呼び出されるものにマップするテーブルを維持する。既定では、SIGINTには、動作は終了するように設定されています。しかし、プログラムは、関数signalの呼び出しによって、いくつかの信号のデフォルト動作を変更することができます。

例: signal(SIGINT, funcHandler)は、SIGINTの受信終了時の動作をfuncHandlerに変更します。しかし、決してfuncHandlerと呼ぶことはありません。 funcHandlerは、プログラムがSIGINTを受け取ったときに呼び出されます。

ここで、プログラムがSIGINTを受信すると、カーネルはどの関数を呼び出すか(ハンドラと呼ぶ)を決定するためにテーブルを調べ、受信した信号を引数としてハンドラのスタックを設定してからプログラムに戻ります。プログラムのコンテキストでは、funcHandlerというエフェクトがsignal_numberを引数として呼び出されます。