POSIX 實時信號

有些亂。。。湊合看把

0. 可靠信號機制原理:
    0.0 當一個信號的出現時, 我們說信號"觸發"了.
    0.1 我們可以爲一個進程指定某個信號相應的 handler.
        有三種特殊的handler : SIG_DFL SIG_IGN SIG_ERR 分別代表: 默認, 忽略, 報錯 這三種handler    
    0.2 當進程收到信號並執行完相應 handler 之後意味着信號已經"發送完畢".
    0.3 信號處於"觸發"後到"發送完畢"之前的時期, 叫做pending .
    0.4 對於某個信號,我們可以進行 block 和 unblock 操作, 注意這跟忽略該信號是不一樣的.
    0.5 在進程 block 某個信號之後, 該進程的這個信號可以由進程自身或其他進程"觸發"一次或多次, 在實時信號被觸發多次的情況下, 這些實時信號在內核維護下形成一個多級優先隊列按先後順序轉發至接受進程:
             0.5.1 按照信號的 signal number 大小排序:
                     小的優先級高排前面, 大的優先級低排後面.
             0.5.2 其次按照信號產生的時間先後對具有相同 signal number 的多個不同信號排序:
                     先產生的在前面, 後產生的在後面.
             0.5.3 但同一時間某一進程既有傳統信號又有實時信號時, 他們由內核發送的順序是未定的.   
    0.6 一個信號一旦產生, 除非目標進程終止或退出之前一直被block, 否則遲早被接受.不存在傳統 UNIX 信號系統的不可靠的缺點.
    0.7 可以通過調用 sigqueue() 觸發信號, 同時還可以在首發進程之間傳遞一個整型數或一個指針
    0.8 注意信號 handler 可以中斷系統調用和庫函數調用, 而且可以在是內核態中斷.  

 

1. 數據結構: 
    1.1 信號集合: bit vector, 每一個位對應一個信號是否 enable
        # define _SIGSET_NWORDS        (1024 / (8 * sizeof (unsigned long int)))
        typedef struct
          {
            unsigned long int __val[_SIGSET_NWORDS];
          } __sigset_t;
        
          typedef __sigset_t sigset_t;

    1.2 信號集合操作:

       #include <signal.h>
        sigset_t set;
       int sigemptyset(sigset_t *set);
       int sigfillset(sigset_t *set);
       int sigaddset(sigset_t *set, int signum);
       int sigdelset(sigset_t *set, int signum);
       int sigismember(const sigset_t *set, int signum);
        返回值: 
               除sigismember外:
                       成功: 0 
                       出錯: -1
               sigismember:
                        是 : 1  
                        否 : 0
                       出錯: -1        
       1.2.1 sigset_t 類型應該在初次使用之前調用sigemptyset() 或 sigfillset() 先初始化.

    1.3 操作進程當前使用信號集合 ( 進程忽略在 mask 集合中的信號 )
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
            how: {SIG_BLOCK,  SIG_UNBLOCK,  SIG_SETMASK}
       
       返回值: 
                        成功: 0 
                       出錯: -1              
                       
    1.4 爲特定信號綁定 handler 
       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
       返回值: 
                        成功: 0 
                         出錯: -1                                   
        1.4.1 數據結構:
               struct sigaction {
               void     (*sa_handler)(int);            
               void     (*sa_sigaction)(int , siginfo_t *, void *); 
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

        注意:
            1.4.1.0 signum 爲除SIGKILL和SIGSTOP外的所有信號.
            1.4.1.1 不可同時指定元素sa_handler 和sa_sigaction(某些架構機器上, 這倆者屬於一個union ).
            1.4.1.2 sa_handler 可爲宏 SIG_DFL:綁定該信號的系統默認handler, SIG_IGN: 忽落該信號.
            1.4.1.3 sa_restorer 現已過時, 不應再使用. POSIX 中並未定義此元素.
            1.4.1.4 可通過第2個參數爲NULL來查詢當前使用的 handler.
            1.4.1.5 可通過指定第2和第3個參數爲NULL來校驗指定的信號在該機器上是否可以使用. 
            1.4.1.6 sa_mask 指定在handler處理信號期間,需要block的信號集合.此外觸發該handler 的
            信號將在handler處理信號期間被屏蔽, 除非在sa_flags中使用了SA_NODEFER 標誌.
                         注意不可屏蔽 
            1.4.1.7 當在元素sa_flags中使用了 SA_SIGINFO 時, 註冊 sa_sigaction 爲信號處理 handler, 
                       而不是 sa_handler
                            sa_sigaction 使用三個參數:
                                     第1個參數,整型, 該信號的整數值;
                                     第2個參數, siginfo_t 指針類型, ;
                                    第3個參數,ucontext_t(轉換爲void 指針)類型.
                            注意: siginfo_t 包含許多可能的屬性,需要針對不同的信號選取對其有意義的屬性. 
                            屬性如下:        
                            siginfo_t {
                                                  /* 對於所有類型信號: */
                                               int      si_signo;    /* Signal number */   
                                               int      si_errno;    /* An errno value ( Linux 中通常未使用此屬性 )*/   
                                               int      si_code;     /*  Signal 的整型值, 而非 bit mask , 表示發生信號的原因 */
                                                                                      /*見 1.4.1.8 描述可能的取值, 以及相應的原因   */
                                                                                      
                                           union /* 針對不同的信號產生方式, 傳遞不同信息給 sa_sigaction */
                                               {
                                                        int _pad[__SI_PAD_SIZE];
                                                
                                                /* 進程調用 kill() 發送.  */
                                                        struct
                                                          {
                                                            __pid_t si_pid;        /* Sending process ID.  */
                                                            __uid_t si_uid;        /* Real user ID of sending process.  */
                                                          } _kill;
                                                
                                                /*計時器 timers (POSIX.1b) .  */
                                                        struct
                                                          {
                                                            int si_tid;                /* Timer ID.  */
                                                            int si_overrun;        /* Timer Overrun count.  */
                                                            sigval_t si_sigval;        /* Signal value.  */
                                                          } _timer;
                                                
                                                /*實時信號 (POSIX.1b).  */
                                                        struct
                                                          {
                                                            __pid_t si_pid;        /* Sending process ID.  */
                                                            __uid_t si_uid;        /* Real user ID of sending process.  */
                                                            sigval_t si_sigval;        /* 發送信號的進程傳來的數據. 參考 sigqueue(3p)  */
                                                                                            /* 子類型:                           */
                                                                                            /*   typedef union sigval           */
                                                                                                         /*    {                                         */
                                                                                                        /*         int   sival_int;  //             */
                                                                                                        /*         void *sival_ptr;  //          */ 
                                                                                                          /*     } sigval_t;                          */ 
                                                          } _rt;
                                                
                                                /* SIGCHLD(通常由子進程退出引起) */
                                                        struct
                                                          {
                                                            __pid_t si_pid;        /* Which child.  */
                                                            __uid_t si_uid;        /* Real user ID of sending process.  */
                                                            int si_status;        /* Exit value or signal.  */
                                                            __clock_t si_utime; /* User time consumed */
                                                            __clock_t si_stime; /* System time consumed */
                                                          } _sigchld;
                                                
                                                /* SIGILL, SIGFPE, SIGSEGV, SIGBUS.  */
                                                        struct
                                                          {
                                                            void *si_addr;        /* Memory location which caused fault */
                                                          } _sigfault;          
                                                
                                                /* SIGPOLL.  */
                                                        struct
                                                          {
                                                            long int si_band;        /* Band event for SIGPOLL.  */
                                                            int si_fd;              /* File descriptor */
                                                            } _sigpoll;
                                               } _sifields;
                                   }
            
            1.4.1.8 關於siginfo_t 類型中  si_code 可取的值, 以及原因.        
         下面是對於一個任何類型的信號,  si_code 可取的值及產生該信號的原因:  
                       SI_USER        kill(2) or raise(3)
                       SI_KERNEL      Sent by the kernel.
                       SI_QUEUE       sigqueue(2)
                       SI_TIMER       POSIX timer expired
                       SI_MESGQ       POSIX 消息隊列狀態改變 (since Linux 2.6.6); see mq_notify(3)
                       SI_ASYNCIO     AIO 完成
                       SI_SIGIO       queued SIGIO
                       SI_TKILL       tkill(2) or tgkill(2) (since Linux 2.4.19)
        
        下面是對於一個 SIGCHLD 信號,  si_code 中可取的值和原因:
                       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 has continued (since Linux 2.6.9)

        下面是對於一個 SIGPOLL 信號,  si_code 中可取的值和原因:
                       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

        下面是對於一個 SIGILL 信號,  si_code 中可取的值和原因:
                       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     coprocessor error
                       ILL_BADSTK     internal stack error

        下面是對於一個 SIGFPE 信號,  si_code 中可取的值和原因:
                       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     floating-point invalid operation
                       FPE_FLTSUB     subscript out of range
                       
        下面是對於一個 SIGSEGV  信號,  si_code 中可取的值和原因:
                       SEGV_MAPERR    address not mapped to object
                       SEGV_ACCERR    invalid permissions for mapped object
                                          
        下面是對於一個 SIGBUS 信號,  si_code 中可取的值和原因:
                       BUS_ADRALN     invalid address alignment
                       BUS_ADRERR     nonexistent physical address
                       BUS_OBJERR     object-specific hardware error

        下面是對於一個 SIGTRAP 信號,  si_code 中可取的值和原因:
                       TRAP_BRKPT     process breakpoint
                       TRAP_TRACE     process trace trap

    1.4.2 注意事項:
    根據POSIX標準, 進程在忽略不是由 kill(2) 或raise(3) 產生的 SIGFPE, SIGILL, 或 SIGSEGV 
    信號時, 其行爲是未定義的 .  整數除零將產生未定義結果. 在某些架構機器上將產生一個
         SIGFPE 信號 (同樣大多負整型數除 -1 也可能產生 SIGFPE.)忽略該信號可能導致死循環.


    1.5 發送信號:
            int sigqueue(pid_t pid, int signo, const union sigval value);      
                成功: 0 
                       出錯: -1                                                     
        1.5.1 sigqueue 函數會立即返回. 
           1.5.1.1如果 pid 進程調用sigaction函數爲 signo 指定的信號綁定handler 時使用了 
                    SA_SIGINFO 標誌,並且系統有相應的資源, 則該信號將加入系統的信號隊列中, 並可
          以被pid 號進程訪問. 
           1.5.1.2若未使用 SA_SIGINFO 標誌, 則至少向 pid 號進程發送一次 signo 信號. 而且此時對
          於是否會向 pid 進程發送 value 參數是未定的.
          1.5.1.3 注意:
        在多線程環境中, ?一個線程調用 sigqueue 向所屬進程發送信號, 
        並且該線程中並未block該信號,
        還有同一進程內也沒有其他的線程要處理這個信號(其它線程block掉了該信號, 或雖
        然沒有block該信號,但也沒有使用 sigwait()來等待該信號), 
     則:
        在本次 sigqueue 調用返回前, 就會向調用線程發送該信號.
    
    1.6 檢查是否有 pending 的信號 
        int sigpending(sigset_t *set);
          成功: 返回0 , 並將 pending 信號集合的 mask 寫入地址 set 中. 
                       出錯: -1
                       
        1.6.1 因爲 set 爲 mask ,而不是直接的集合.
        所以, 在判斷一個具體的信號是否有pending 的信號時應該如下使用,以SIGSTOP 爲例:
                    sigset_t set;
                    Hsigemptyset(&set);
                    Hsigpending(&set);
                    if(  !sigismember( &set, signum) ) printf("There is pending SIGSTOP signals" );

     1.7 等待信號, 掛起線程:
           int sigwait(const sigset_t *set, int *sig);
      返回值:
          成功: 0 . 
                       出錯:大於零的一個錯誤值. 

        1.7.1 掛起調用線程並等待, 直到收到一個在指定集合 set 中的信號時返回, 同時從pending 的
        信號?列表中移除本次收到的這個信號, 並返回該信號數值到第二個參數 sig 中.
        1.7.2 sigwait() 和 sigwaitinfo() 的區別在於:
                    sigwait()只根據 signal number 來識別需要等待的信號並返回本次信號的signal number. 
                    sigwaitinfo() 則可以根據 siginfo_t 來識別需要等待的信號並返回本次信號的siginfo_t.
        就是說 sigwaitinfo()  可以精確的指定需要等待的信號類型, 但也更加重量級   

     1.8 等待信號, 掛起調用進程:
           int sigwaitinfo(const sigset_t *set, siginfo_t *info);
           int sigtimedwait(const sigset_t *set, siginfo_t *info,
                        const struct timespec *timeout);
      返回值:
          成功: 本次信號的 signal number (大於零).   並從pending 信號列表中移除本次信號.        
                       出錯: -1.                         
                 而且當sigtimedwait() 在設定時間內未接受到信號會設置 errono 爲 EAGAIN .
        1.8.1   sigtimedwait() 除了多使用了一個超時參數 timeout 外, 其他都和sigwaitinfo 一樣.
                    struct timespec {
                       long    tv_sec;         /* seconds */
                       long    tv_nsec;        /* nanoseconds */
                   }
        當參數timeout 中兩個參數都指定爲 0 時,進行 poll 操作, 即 sigtimedwait()立即返回,並且:
            在檢查信號queue之前, 其中已有要等待的信號, 立即返回該信號的 siginto_t 信息.
                         queue 中一個也沒有所要等待的信號時, 立即返回error.
                        
     1.9 等待信號, 掛起調用進程,:
            int sigsuspend(const sigset_t *mask);
      返回值:
          成功: -1, 通常 errno 還被置爲 EINTR . 
                若該進程掛起後, 接受的是終止類型的信號則該進程被終止.
                接受的是非終止類型的信號, 則該進程執行相應的handler.
                所以這個函數返回值很怪異.
                       出錯: errno  被置爲 EFAULT  (  參考 sigsuspend(3p) );
                       
        1.8.1 注意:
        通常 sigsuspend 會和sigprocmask 一起使用. 這樣可以預防在程序的關鍵代碼段執行期
        間受某些信號影響:
                  1.8.1.1 首先進程在執行關鍵代碼之前調用sigprocmask 來blocks 某些信號. 並且將
               執行block 操作之前的信號集合保存到 sigprocmask 的 oldset 中.
                  1.8.1.2 其次在關鍵代碼執行完後, 該進程調用 sigsuspend  並傳遞之前調用
                               sigprocmask時保存在它的第三個參數 oldset 中的信號集合給 sigsuspend.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章