§3.割込
 

3.1 SVR3 UNIXの割込
 UNIXの割込機構には、以下のような不具合がありました。

  1. シグナルがキューイングされないため、送信した数だけ配送される保証がありません。また、シグナルハンドラ実行中に同一シグナルをブロックできません。このため 以下の例ではプログラムが第2行目にいる時に、SIGQUITシグナルをキャッチすると、デフォルトの処理(コアダンプ)となり終了してしまいます。
    1. catch_sigquit()
    2. {
    3.     signal(SIGQUIT, catch_sigquit);
    4.     ・・・・・・・・・・・
    5. }
    6. main()
    7. {
    8.     signal(SIGQUIT, catch_sigquit);
    9.     ・・・・・・・・・・・・・
    10. }
  2. タイミングは通知できるが、情報をわたすことができません。
  3. プライオリティ付けができません。
  4. シグナルによって中断したシステムコールを再実行できません。
3.2 SVR4の対応

 これらの対策としてSVR4では様々な対策がとられ、多くの改善が行われました。
例えば上記(1)の問題に対しては、signal(2) 関数の代わりに、 sigprocmask(2)、sigaction(2) 関数の導入で対応しました。これらは、シグナルを受け取った時にその番号のシグナルをマスクし、同一シグナルの処理をブロックすることができるようになりました。
デフォルト処理を行う代わりにハンドラ処理の終了を待ちます。また、(2)のデータ授受と(4)の再実行に関しては、 sigaction(2) によって可能となりました。さらに、リアルタイムシグナルとsigqueue(3)を使用することでシグナル番号に応じたプライオリティ付けも行えます。

 SVR4で導入されたシグナルマスクは、シグナル番号に対応した0/1のフラグです。シグナルの数が32以上ある場合もあるので、必ずしもlong intでないことに注意してください。このシグナルマスクのためにsigset_t型が定義されています。
シグナルマスクは、sigprocmask()で登録しますが、マスクの値の設定は以下のマクロ関数で行います。

    sigset_t  signal_masks;

    sigemptyset(&signal_masks);                シグナルマスクをすべて0に設定する。
    sigfillset(&signal_masks);                     シグナルマスクをすべて1に設定する。
    sigdelset(&signal_masks,シグナル番号);シグナル番号のシグナルマスクを0に設定する。
    sigaddset(&signal_masks,シグナル番号);シグナル番号のシグナルマスクを1に設定する。

シグナルマスクが1に設定された場合そのシグナルはブロックされるため、受信はペンディングされ、シグナルマスクが0になった時点で配信されます。
 この場合、ペンディングされるのみでキューイングされないため、1回以上のシグナルが配信された場合、そのイベントは消失してしまいます。

sigprocmask()関数は、以下のパラメータを用い、シグナルマスクをセットしたり、リセットします。

 int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

関数の動作を決めるのは、1番目のパラメータで、以下の値を使います。

SIG_BLOCK        2番目のパラメータで指示した値(1の部分)をシグナルマスクに加える(sigaddset)
SIG_UNBLOCK    2番目のパラメータで指示した値(1の部分)をシグナルマスクからのぞく(0にする)
SIG_SETMASK  2番目のパラメータで指示した値を新しいシグナルマスクにする

現在のシグナルマスクの値を得たい場合には、SIGSETMASKを指示し、セカンドパラメータをNULLに設定すると、3番目のパラメータに現在のシグナルマスクが帰ってきます。このときシグナルマスクは変更されません。

 また、現在のシグナルマスクにシグナルをセットしたり、リセットしたりするには、以下の関数も利用できます。

     sighold(シグナル番号)     シグナルマスクにシグナル番号を加える.

     sigrelse(シグナル番号)     シグナルマスクからシグナル番号を取り除く.

     sigignore(シグナル番号)   シグナルハンドラを SIG_IGN(シグナルを無視)に設定する
 

3.3 POSIX1003.1bのキュードシグナル

 PowerMAX/OSでは、POSIX1003.1bリアルタイム拡張が行われています。シグナルのキューイングは以下の関数で実現しています。

   int sigqueue(pid_t pid, int signo, const union sigval value )

sigqueue(2) は、signo で指定されたシグナルを、変数 value とともに、pidで示されたプロセスへ通知します。受取り側のプロセスでは、予めsigaction(2) で指定した処理が実行されます。又シグナル番号も、POSIX1003.1b定義のシグナルを追加しています。現バージョンでは、以下の8種類が登録されており、これらは番号が大きいほど高いプライオリティとなっています。

      SIGRTMIN   (35)
      SIGRTMIN+1 (36)
      SIGRTMIN+2 (37)
          :
      SIGRTMIN+6 (41)
      SIGRTMAX   (42)

sigqueue()で送信される値は、受信側のsigactionのフラグにSA_SIGINFOを指示することで受信できます。

union sigvalは、以下のように定義されています。

union sigval {
        int     sival_int;      /* integer value */
        void    *sival_ptr;     /* pointer value */
};

したがって、int型の値を代入する場合には、

sigval_t value;

 value.sival_int = 0;

また、ポインタ型を代入する場合には、

value.sival_ptr = (void *)NULL;

のように利用します。

3.4 シグナルハンドラの登録

 シグナルハンドラの登録は、sigaction()を用います。
登録するシグナルハンドラは、以下の定義に従う必要があります。

タイプ1:古いインターフェース(シグナル番号のみ)

void  signal_handler(int sig);

タイプ2:新しいインターフェース(シグナル番号以外の拡張データを引き渡す)

void  signal_handler(int sig, siginfo_t *sip, ucontext_t *uc);

この2つの定義の切り替えは、シグナルハンドラ定義のunion型で使い分けます。

           union {
               void         (*sa_handler)(int);
               void         (*sa_sigaction)(int,siginfo_t *, void *);
          } sa_func;
          sigset_t     sa_mask;
          int          sa_flags;

          #define sa_handler  sa_func.sa_handler
          #define sa_sigaction     sa_func.sa_sigaction
 

sigaction()による、シグナルハンドラ定義には、sigaction構造体を使用します。

struct sigaction newact, oldact;

sigaction構造体は、内部に以下のフィールドを持っているのでそれぞれ正しい値に初期化して呼び出す必要があります。
以下のタイプ1の例では、フラグになにも設定していませんが、タイプ2で使用しているフラグを設定してもかまいません。

タイプ1:古いインターフェース(シグナル番号のみ)

    sigemptyset(&newact.sa_mask);                   0クリアする
    sigaddset(&newact.sa_mask,SIGRTMIN);        SIGRTMINがシグナルハンドラ内でブロックされるようにする
                                                                 これを行わないとハンドラ内で、同一シグナルを受信してしまう。
    newact.sa_handler = signal_handler;             古いインターフェースでシグナルハンドラを登録する。
    newact.sa_flags = 0;                                   新しいインターフェースは使用しない。
    sigaction (SIGRTMIN, &newact, &oldact);      SIGRTMINのシグナルハンドラを登録する。

タイプ2:新しいインターフェース(シグナル番号以外の拡張データを引き渡す)

    sigemptyset(&newact.sa_mask);                    0クリアする
    sigaddset(&newact.sa_mask,SIGRTMIN);         SIGRTMINがシグナルハンドラ内でブロックされるようにする
                                                                  これを行わないとハンドラ内で、同一シグナルを受信してしまう。
    newact.sa_sigaction = signal_handler;            新しいインターフェースでシグナルハンドラを登録する。
    newact.sa_flags = SA_SIGINFO|SA_RESTERT; 新しいインターフェースの拡張データを受信し、割り込まれたシステムコールは再実行する。
    sigaction (SIGRTMIN, &newact, &oldact);      SIGRTMINのシグナルハンドラを登録する。
 
 

以下に、コーディング例の主要部分を示します。

#include <sys/signal.h>
#include <sys/siginfo.h>
#include <siginfo.h>

void *task1()
{
  struct sigaction newact, oldact;
   void signal_handlerr(int,siginfo_t *, void *);
   :
   newact.sa_sigaction = signal_handler;            /* signal handler  */
   sigemptyset(&newact.sa_mask);                   /* get signal mask */
   sigaddset(&newact.sa_mask,SIGRTMIN);
   newact.sa_flags = SA_SIGINFO|SA_RESTART; /* 2nd & 3id arg valid */
   sigaction (SIGRTMIN, &newact, &oldact);
      :
}

void *task2()
{
   :
   sigval_t value;
      :
   value.sival_int = 1000;
   if(( error = sigqueue( pid_tsk1, SIGRTMIN, value )) != 0 )
   {
           perror(" sigqueue error ");
           exit();
  }
      :
}

3.5 シグナルの拡張情報

 プロセスがシグナルを受信した場合には、システムがそのシグナルを生成した理由を告げる拡張情報与えます。  もしプロセスがそのチャイルドプロセスをモニタしているなら、それは子供が状態を変えた理由の情報を受け取ります。  どちらの場合も、システムは次の情報を含むタイプ siginfo_t のストラクチャで情報を返します。

         int si_signo   /* signal number */
          int si_errno   /* error number */
          int si_code    /* signal code */

 si_signo がシステムによって生成されたシグナル番号を示します。   ( waitid (2)機能の場合には、 si_signo は常に SIGCHLD です)

     si_errno がゼロ以外であるなら、それは、 errno.h で定義されるように、このシグナルと結び付けられたエラー番号を示します。
     si_code がシグナルの原因を識別しているコードを含んでいます。
     si_code の値が0かそれ以下であるなら、シグナルはユーザープロセスによって生成されました。

     もし si_code がゼロと等しいなら、それはkill(2)あるいは sigsend (2) によって送られました、そして siginfo 構造体は次の追加の情報を含んでいます。

         pid_t si_pid   /* sending process ID */
          uid_t si_uid   /* sending user ID */

     もし si_code が SI_QUEUE 、 SI_TIMER 、 SI_ASYNCIO あるいは SI_MESGQ のひとつであるなら、シグナルは posix realtime 拡張関数によってユーザープロセスによって生成されました、そしてkill()やsigsend()によって生成されたシグナルのための情報提供のほかに、 siginfo 構造体は同じく次のインフォメーションを含んでいます:

          int si_value   /* 任意の値 */

  si_value フィールドは送信側が定義した任意の情報を含んでいます。  例えばSI_ASYNCIO のために利用される、非同期の終了によって影響を与えられるデータ構造のアドレスであるかもしれません

     もし si_code が負の値ではなく、ゼロ以外であるなら、それは以下のようなシグナルが、生成された理由を示しています。

Signal    Code            Reason
   ________________________________________________________________
   SIGILL
             ILL_ILLOPC      illegal opcode
             ILL_ILLOPN      illegal operand
             ILL_ILLADR      illegal addressing mode
             ILL_ILLTRP      illegal trap
             ILL_PRVOPC      privileged opcode
             ILL_PRVREG      privileged register
             ILL_COPROC      co-processor error
             ILL_BADSTK      internal stack error
   ________________________________________________________________
   SIGFPE
             FPE_INTDIV      integer divide by zero
             FPE_INTOVF      integer overflow
             FPE_FLTDIV      floating point divide by zero
             FPE_FLTOVF      floating point overflow
             FPE_FLTUND      floating point underflow
             FPE_FLTRES      floating point inexact result
             FPE_FLTINV      invalid floating point operation
             FPE_FLTSUB      subscript out of range
   ________________________________________________________________
   SIGSEGV   SEGV_MAPERR     address not mapped to object
             SEGV_ACCERR     invalid permissions for mapped object
   ________________________________________________________________
   SIGBUS
             BUS_ADRALN      invalid address alignment
             BUS_ADRERR      non-existent physical address
             BUS_OBJERR      object specific hardware error
             BUS_XMEM        reservation instruction to I/O address
   ________________________________________________________________
   SIGTRAP
             TRAP_BRKPT      process breakpoint
             TRAP_TRACE      process trace trap
   ________________________________________________________________
   SIGCHLD
             CLD_EXITED      child has exited
             CLD_KILLED      child was killed
             CLD_DUMPED      child terminated abnormally
             CLD_TRAPPED     traced child has trapped
             CLD_STOPPED     child has stopped
             CLD_CONTINUED   stopped child had continued
   ________________________________________________________________
   SIGPOLL
             POLL_IN         data input available
             POLL_OUT        output buffers available
             POLL_MSG        input message available
             POLL_ERR        I/O error
             POLL_PRI        high priority input available
             POLL_HUP        device disconnected

 以下のシグナルではcoreの生成された追加情報を示します。

Signal         Field                            Value
_________________________________________________________________________
SIGILL    caddr_t si_addr   address of faulting instruction
SIGFPE
_________________________________________________________________________
SIGSEGV   caddr_t si_addr   address of faulting memory reference
SIGBUS
_________________________________________________________________________
SIGCHLD   pid_t si_pid      child process ID
                int si_status   exit value or signal
_________________________________________________________________________
SIGPOLL   long si_band      band event for POLL_IN, POLL_OUT, or POLL_MSG

以下に、siginfo構造体の完全なストラクチャを示します。

typedef struct siginfo {

        int     si_signo;                       /* signal from signal.h */
        int     si_code;                        /* code from above      */
        int     si_errno;                       /* error from errno.h   */

        union {

                struct {                        /* kill(), SIGCLD       */
                        pid_t   _pid;           /* process ID           */
                        union {
                                struct {
                                        uid_t   _uid;
                                        int     _value;
                                } _kill;
                                struct {
                                        ulong_t _reserved1;
                                        int     _status;
                                } _cld;
                        } _pdata;
                } _proc;

                struct {        /* SIGSEGV, SIGBUS, SIGILL and SIGFPE   */
                        caddr_t _addr;          /* faulting address     */
                } _fault;

                struct {                        /* SIGPOLL, SIGXFSZ     */
                /* fd not currently available for SIGPOLL */
                        int     _fd;            /* file descriptor      */
                        long    _band;
                } _file;

        } _data;

} siginfo_t;
#define si_pid          _data._proc._pid
#define si_status       _data._proc._pdata._cld._status
#define si_uid          _data._proc._pdata._kill._uid
#define si_value        _data._proc._pdata._kill._value
#define si_addr         _data._fault._addr
#define si_fd           _data._file._fd
#define si_band         _data._file._band

3.6 シグナルの受信

 シグナルの受信関数には、いくつかの種類が用意されています。
いずれの動作も、シグナルマスクを操作し、関数内で一時的にブロックを解除してその間シグナルを受信するようになっています。

int sigsuspend(const sigset_t *set);

sigsuspend()は、シグナルマスクを現在設定しているものから、関数で指示されたものへ一時的に置き換え、シグナルの発生を待ちます。
シグナルを受信した場合、シグナルハンドラが起動され、その後、シグナルマスクは、元のシグナルマスクの値に戻されます。

int sigpause(int sig);

sigpause()は、シグナルマスクを現在設定しているものから、関数で指示されたものへ一時的に置き換え、シグナルの発生を待ちます。
シグナルを受信した場合、シグナルハンドラが起動されず、その後、シグナルマスクは、元のシグナルマスクの値に戻されます。

int sigwait(sigset_t *set, int *sig);

sigwait()は、シグナルマスクを不可分な操作で置き換え、選択されたシグナルが到着するまでs、呼び出したプロセスの実行を停止します。
 もしシグナルが呼び出しの時にペンディングされていないならば1つあるいはそれ以上のシグナルが配信されるまで、呼び出したプロセスは実行を停止され、 この停止の時間は不定です。
 シグナルマスクはリターン時に呼び出し前の値に戻されます。注意事項として、結果が予想できないため、アプリケーションプログラムは sigwait() と sigaction() の混在しての使用はさけなければなりません。

int sigtimedwait(const sigset_t *set,siginfo_t *info,const struct timespec *timeout)

 sigtimedwait () は、シグナルマスクで指示された、最も低い値のシグナル番号からペンディングシグナルを選択します。
もし指示されたシグナルマスクに対応するシグナルが、呼び出しの時に、保留されていないならば、1つあるいはそれ以上のシグナルが配信されるなるまで、あるいはタイムアウトアーギュメントによって指定された時間が経過するまで、停止されます。
 もしタイムアウトアーギュメントが 0の値のtimespec ストラクチャを参照するか、setによって指定されたシグナルのいずれも設定されていないならば、 sigtimedwait () がエラーですぐに戻ります。あるいはタイムアウトがゼロのポインタであるなら、関数は無限の待ち状態におかれます。

     もしインフォメーションアーギュメントがゼロではないなら、配信されたシグナル番号は si_signo メンバーにストアされます、そしてシグナルの原因は si_code メンバーにしまっておかれます。 もし値が配信されたシグナルによって待ち行列に入れられているならば、待ち行列にからデキュー されます。
 そして、もしインフォメーションアーギュメントがゼロでないなら、値はインフォメーションの si_value メンバーにしまっておかれます。  シグナルを待ち行列に入れるえ使われたシステムリソースは解放されます。  もし値が待ち行列に入れられないなら、 si_value メンバーの内容は不確定です。

int sigwaitinfo(const sigset_t *set,siginfo_t *info)

     sigwaitinfo () 機能は3番目のアーギュメントがNULLの sigtimedwait () と等価です。