天天看點

linux信号機制之sigaction結構體淺析,signal 函數,信号捕捉

來自:http://hi.baidu.com/phenix_yw/blog/item/6eb4ca391d1479f23a87ce19.html

信号安裝函數sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)的第二個參數是一個指向sigaction結構的指針(結構體名稱與函數名一樣,千萬别弄混淆了)。在結構sigaction的執行個體中, 指定了對特定信号的處理,信号所傳遞的資訊,信号處理函數執行過程中應屏蔽掉哪些函數等。當然,此指針也可以為NULL,程序會以預設方式處理信号。以下 就簡單介紹一下sigaction結構以及一般的用法。

        對于核心頭檔案而言,struct sigaction 結構體定義在kernel/include/asm/signal.h,此頭檔案又被kernel/include/linux/signal.h包含。

        對于使用者空間的頭檔案而言,struct sigaction定義在 /usr/include/bits/sigaction.h,此頭檔案又被/usr/include/signal.h包含,是以應用程式中如果用到此 結構,隻要#include <signal.h>即可。注意核心中的定義和應用程式中的定義是不一樣的,核心空間的sigaction結構隻支援函數類型為 __sighandler_t的信号處理函數,不能處理信号傳遞的額外資訊。具體定義如下:

……

typedef void (*__sighandler_t)(int);

……

#ifdef __KERNEL__

struct old_sigaction {

          __sighandler_t sa_handler;

         old_sigset_t sa_mask;

         unsigned long sa_flags;

         void (*sa_restorer)(void);

};

struct sigaction {

         __sighandler_t sa_handler;

        unsigned long sa_flags;

        void (*sa_restorer)(void);

        sigset_t sa_mask;  

};

struct k_sigaction {

        struct sigaction sa;

};

#else

struct sigaction {

          union {

                  __sighandler_t _sa_handler;

                  void (*_sa_sigaction)(int, struct siginfo *, void *);

          } _u;

          sigset_t sa_mask;

          unsigned long sa_flags;

          void (*sa_restorer)(void);

};

#define sa_handler   _u._sa_handler

#define sa_sigaction _u._sa_sigaction

#endif

sa_handler的原型是一個參數為int,傳回類型為void的函數指針。參數即為信号值,是以信号不能傳遞除信号值之外的任何資訊;

sa_sigaction的原型是一個帶三個參數,類型分别為int,struct siginfo *,void *,傳回類型為void的函數指針。第一個參數為信号值;第二個參數是一個指向struct siginfo結構的指針,此結構中包含信号攜帶的資料值;第三個參數沒有使用。

sa_mask指定在信号處理程式執行過程中,哪些信号應當被阻塞。預設目前信号本身被阻塞。

sa_flags包含了許多标志位,比較重要的一個是SA_SIGINFO,當設定了該标志位時,表示信号附帶的參數可以傳遞到信号處理函數中。即 使sa_sigaction指定信号處理函數,如果不設定SA_SIGINFO,信号處理函數同樣不能得到信号傳遞過來的資料,在信号處理函數中對這些信 息的通路都将導緻段錯誤。

sa_restorer已過時,POSIX不支援它,不應再使用。

        是以,當你的信号需要接收附加資訊的時候,你必須給sa_sigaction賦信号處理函數指針,同時還要給sa_flags賦SA_SIGINFO,類似下面的代碼:

     #include <signal.h>

     ……

     void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}

     int main(int argc,char **argv)

     {

              struct sigaction sig_act;

              ……

              sigemptyset(&sig_act.sa_mask);

              sig_act.sa_sigaction=sig_handler_with_arg;

              sig_act.sa_flags=SA_SIGINFO;

               ……

     }

        如果你的應用程式隻需要接收信号,而不需要接收額外資訊,那你需要的設定的是sa_handler,而不是sa_sigaction,你的程式可能類似下面的代碼:

     #include <signal.h>

     ……

     void sig_handler(int sig){……}

     int main(int argc,char **argv)

     {

              struct sigaction sig_act;

              ……

              sigemptyset(&sig_act.sa_mask);

              sig_act.sa_handler=sig_handler;

              sig_act.sa_flags=0;

               ……

      }

      如果需要更詳細說明,請參閱sigaction的man手冊。

補充:

簡而言之就是:

//自定義退出函數

    sigact.sa_handler = mysighandler;

    sigemptyset(&sigact.sa_mask);

    sigact.sa_flags = 0;

    sigaction(SIGINT, &sigact, NULL);

    sigaction(SIGTERM, &sigact, NULL);

    sigaction(SIGQUIT, &sigact, NULL);

或者利用signal函數進行信号捕捉:

void (*signal(int signo, void (*handler)(int)))(int);


      
當signal到來時,程式運作某函數,函數由你自己指定。



附帶各種信号定義:

      

在終端使用kill -l 指令可以顯示所有的信号。

$kill -l

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL

5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE

9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2

13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT

17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU

25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH

29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN

35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4

39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8

43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12

47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14

51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10

55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6

59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2

63) SIGRTMAX-1 64) SIGRTMAX

其中前面31個信号為不可靠信号(非實時的,可能會出現信号的丢失),後面的信号為可靠信号(實時的real_time,對信号

排隊,不會丢失)。

1) SIGHUP (挂起) 當運作程序的使用者登出時通知該程序,使程序終止

2) SIGINT (中斷) 當使用者按下時,通知前台程序組終止程序

3) SIGQUIT (退出) 使用者按下或時通知程序,使程序終止

4) SIGILL (非法指令) 執行了非法指令,如可執行檔案本身出現錯誤、試圖執行資料段、堆棧溢出

5) SIGTRAP 由斷點指令或其它trap指令産生. 由debugger使用

6) SIGABRT (異常中止) 調用abort函數生成的信号

7) SIGBUS 非法位址, 包括記憶體位址對齊(alignment)出錯. eg: 通路一個四個字長的整數, 但其位址不是4的倍數.

8) SIGFPE (算術異常) 發生緻命算術運算錯誤,包括浮點運算錯誤、溢出及除數為0.

9) SIGKILL (确認殺死) 當使用者通過kill -9指令向程序發送信号時,可靠的終止程序

10) SIGUSR1 使用者使用

11) SIGSEGV (段越界) 當程序嘗試通路不屬于自己的記憶體空間導緻記憶體錯誤時,終止程序

12) SIGUSR2 使用者使用

13) SIGPIPE 寫至無讀程序的管道, 或者Socket通信SOCT_STREAM的讀程序已經終止,而再寫入。

14) SIGALRM (逾時) alarm函數使用該信号,時鐘定時器逾時響應

15) SIGTERM (軟中斷) 使用不帶參數的kill指令時終止程序

17) SIGCHLD (子程序結束) 當子程序終止時通知父程序

18) SIGCONT (暫停程序繼續) 讓一個停止(stopped)的程序繼續執行. 本信号不能被阻塞.

19) SIGSTOP (停止) 作業控制信号,暫停停止(stopped)程序的執行. 本信号不能被阻塞, 處理或忽略.

20) SIGTSTP (暫停/停止) 互動式停止信号, Ctrl-Z 發出這個信号

21) SIGTTIN 當背景作業要從使用者終端讀資料時, 終端驅動程式産生SIGTTIN信号

22) SIGTTOU 當背景作業要往使用者終端寫資料時, 終端驅動程式産生SIGTTOU信号

23) SIGURG 有"緊急"資料或網絡上帶外資料到達socket時産生.

24) SIGXCPU 超過CPU時間資源限制. 這個限制可以由getrlimit/setrlimit來讀取/改變。

25) SIGXFSZ 當程序企圖擴大檔案以至于超過檔案大小資源限制。

26) SIGVTALRM 虛拟時鐘信号. 類似于SIGALRM, 但是計算的是該程序占用的CPU時間.

27) SIGPROF (梗概時間逾時) setitimer(2)函數設定的梗概統計間隔計時器(profiling interval timer)

28) SIGWINCH 視窗大小改變時發出.

29) SIGIO(異步I/O) 檔案描述符準備就緒, 可以開始進行輸入/輸出操作.

30) SIGPWR 電源失效/重新開機動

31) SIGSYS 非法的系統調用。

在以上列出的信号中,

程式不可捕獲、阻塞或忽略的信号有:SIGKILL,SIGSTOP

不能恢複至預設動作的信号有:SIGILL,SIGTRAP

預設會導緻程序流産的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ

預設會導緻程序退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM

預設會導緻程序停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU

預設程序忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH

此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在程序挂起時是繼續,否則是忽略,不能被阻塞。

在Unix/Linux中signal函數是比較複雜的一個,其定義原型如下: void (*signal(int signo,void (*func)(int))) (int) 這個函數中,最外層的函數體 void (* XXX )(int)表明是一個指針,指向一個函數XXX的指針,XXX所代表的函數需要一個int型的參數,傳回void signal(int signo, void(*func)(int))是signal函數的主體. 需要兩個參數int型的signo以及一個指向函數的函數. void (*func)(int). 正是由于其複雜性,在[Plauger 1992]用typedef來對其進行簡化 typedef void Sigfuc(int);//這裡可以看成一個傳回值 . 再對signal函數進行簡化就是這樣的了 Sigfunc *signal(int,Sigfuc *);   在signal.h頭檔案中還有以下幾個定義 #define SIG_ERR (void (*)())-1 #define SIG_DFL (void (*)())0 #define SIG_IGN (void (*)())1

繼續閱讀