2

Cプログラミングで信号を学ぼうとしていますが、いくつかの概念を理解するのに苦労しています. 私はこのコードを利用できるので、誰かが私を案内してくれることを望んでいました. 私は主にmain()、長い形式と短い形式の違いを理解しようとしています。

  1. 長い形式で何SA_SIGINFO | SA_RESTART;をしますか?
  2. 1 つのハンドラー関数を使用action.sa_sigactionして長い形式で初期化し、別のシグナル ハンドラーを使用して初期化するのはなぜaction.sa_handlerですか?
#include    <errno.h>
#include    <math.h>
#include    <stdlib.h>
#include    <signal.h>
#include    <stdio.h>
#include    <unistd.h>
#include    <sys/time.h>
#include    "printBlocked.h"

// Timer demonstration of signal handling.  Timer intervals are random.

static int
bkr_random( int lo, int hi )
{
    static int  seed = 2*3*5*7*11*13*17*19+1;
    static int  prime = 2*3*5*7*11*13*17+1;
    int     value;

    value = lo + (seed>>3) % (hi-lo+1);
    seed *= prime;
    seed = seed < 0 ? -seed : seed;
    return value;
}

static void
timeout_handler( int signo, siginfo_t * info , void * p )
{
    struct itimerval    interval;

    printf( "timeout_handler() invoked.  errno is %d\n", info->si_errno );
    printBlocked( "timeout handler" );
    interval.it_interval.tv_usec = 0;               /* No repeat interval */
    interval.it_interval.tv_sec = 0;
    interval.it_value.tv_sec = 0;
    interval.it_value.tv_usec = 25000 * bkr_random( 1, 39 );    /* reset interval each time */
    setitimer( ITIMER_REAL, &interval, 0 );
}

static void
signal_handler( int signo )
{
    switch ( signo )
    {
        case SIGINT:
            printf( "Signal handler invoked.  Delivered signal is %s.\n", _sys_siglist[signo] );
            printBlocked( "signal handler" );
            exit( 0 );
            break;
        case SIGWINCH:
            system( "clear" );
            printf( "Signal handler invoked.  Window size changed.\n" );
            printBlocked( "signal handler" );
            break;
        default:
            printf( "Signal handler invoked.  unknown signal delivered is %s.\n", _sys_siglist[signo] );
            printBlocked( "signal handler" );
            break;
    }
}

int
main()
{
    struct sigaction    action;
    struct itimerval    interval;

    printf( "main() invoked in process %d.\n", getpid() );
    printBlocked( "main()" );

    action.sa_flags = SA_SIGINFO | SA_RESTART;  /* asking for long form and abilty to continue normal execution */
    action.sa_sigaction = timeout_handler;      /* long form */
    sigemptyset( &action.sa_mask );         /* no additional signals blocked */
    sigaction( SIGALRM, &action, 0 );

    action.sa_flags = 0;
    action.sa_handler = signal_handler;     /* short form */
    sigemptyset( &action.sa_mask );         /* no additional signals blocked */
    sigaction( SIGINT, &action, 0 );
    sigaction( SIGWINCH, &action, 0 );

    interval.it_interval.tv_sec = 0;                /* No repeat interval */
    interval.it_interval.tv_usec = 0;
    interval.it_value.tv_usec = 25000 * bkr_random( 1, 39 );    /* initial interval */
    interval.it_value.tv_sec = 0;
    setitimer( ITIMER_REAL, &interval, 0 );

    while( pause() == -1 );             /* wait for a signal, any signal */
    printf( "Normal end.\n" );
}
4

1 に答える 1

4

一般に、 のドキュメントを参照してくださいsigaction

具体的には、

SA_SIGINFO | SA_SIGINFO とは何ですか? SA_RESTART; 長い形式で行いますか?

SA_SIGINFO はsa_sigaction、「短い形式」のハンドラーではなく「長い形式」のハンドラーを呼び出すようにシステムに指示しますsa_handler

SA_RESTART は、シグナル ハンドラから戻ったときに特定のシステム コールを再開するようにシステムに指示します。そうしないと、これらの呼び出しは通常、errno が EINTR に設定されて失敗します。これは、SA_SIGINFO が設定されているかどうかに関係なく適用されます。

*1 つのハンドラー関数を使用して action.sa_sigaction を長い形式で初期化し、別のシグナル ハンドラーを使用して action.sa_handler を初期化するのはなぜですか?*

sa_sigaction通常、追加siginfo_t情報を渡す必要がある場合に使用されます。この場合、表示するコードの作成者は、シグナル生成に関連付けられた errno を知りたがっています ( si_errno)。現在、私のシステムでは、表示されているコードでそのフィールドは常にゼロになります。自分で設定したアラームに関連付けられた errno も、(kill(1)コマンドのように) 外部に配信されたアラームにも関連付けられていません。

他のシグナル アクションはそのような追加情報を必要としないため、より単純なsa_handlerインターフェイスを使用しても害はありません。

(余談ですが、適切なシグナル処理の実践では、ハンドラーをasync-signal-safeである呼び出し関数に制限します。投稿するコードはそれを行いません。)

于 2013-11-07T20:53:57.927 に答える