天天看點

第13章 信号及信号處理

一、信号的基礎。

  1、信号的基本概念。

@可以用kill -l來檢視系統中的信号,這些信号的定義在signal.h檔案中。

@信号的編号沒有0信号,是從1開始的。

2、産生信号。

  @産生程序有5種方式。

  使用者按下按鍵;硬體異常産生信号(被0除);調用kill(2)函數可以發送信号給另一個程序;kill(1)指令發送信号;核心檢測到某種軟體條件發生。如alarm

@當就緒或者是阻塞的程序,遇到信号都會被激活來處理信号。

3、處理信号。

@有三種處理方法。忽略。注冊一個處理函數。系統預設執行。

4、常用信号的使用方法。

@Ctrl +c向目前程序發送一個SIGINT信号。

@Ctrl +/ 使程式結束運作,而産生core檔案。可以利用gdb來調試core檔案。

二、信号的影響。

1、重入。

    一個函數符合以下條件之一都是不可重入的。

@使用全局變量。例如全局變量或靜态變量。

@調用了動态方法得到記憶體。

@使用了标準的IO庫。标準的IO庫的很多實作都以不可重入的方式使用全局資料結構。

總結:使用全局的東西的函數都不行。稱為非純代碼。

2、原子操作。

如果多個流程通路一個全局資源,那麼這個全局要定義為:

 如: volatile sig_atomic_t a=0;

3、中斷系統調用。

@使用sigactions可以設定信号處理程式傳回時是否重新啟動被中斷的系統調用。

三、信号處理函數。

1、設定信号處理函數。

@使用signal函數來處理函數加載。

  #include<signal.h>

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

  第一個參數,表示要加載處理的信号的編号。例如:SIGKILL其本質是整數。

第二個參數,表示一個函數的指針。也可以是以下三個之一。

SIG_IGN:表示忽略該信号。(STGKILL SIGSTOP信号不夠忽略。)

SIG_DFL表示使用預設的信号處理方式。

用定義好的函數指針。

一般用法:

void signal_handle(int signo)

{

    switch(signo){

   case SIG :

。。。。。

}

@有兩個信号沒有特殊的意義的:SIGUSR1,SIGUSR2

2、發送信号。

@使用kill向程序或程序組發送信号。

#include<signal.h>

int kill(pid_t pid, int signo);

pid有4種不同的寫法。

大于0:信号發給pid 的程序。

等于0:發送給程序組ID和該程序相同的程序。

小于0:發送給程序組内程序ID為pid的程序。

等于-1:信号發送給所有的程序。

注意事項:

@系統程序不能接收信号。

程序之間發送信号需要權限的檢查。

(1)uid=uid:可以,同一使用者的程序之間的信号通信。

 (2)uid=eid:接收者得到了發送者的授權,暫時成為與發送者同一個使用者的程序。

 (3)eid=uid:發送者得到接收者的暫時成為與接收者同一個使用者的程序。

   (4)eid=eid:發送者的接收者都暫時屬于同一使用者,等同地第一種情況。

根使用者可以向任何使用者發送信号。如果成功則傳回0,不成功傳回-1,

@可以向自己發送信号。如kill(getpid(),signo);

3、向程序本身發送信号。

int raise(int signo);

@可以使程序退出。但是隻是沒有做善後的工作。如關閉檔案,沖洗流。

4、設定Linux定時器。

@#include<unistd.h>

uinsigned int alarm(unsigned int seconds);

@當系統時間超過該時間後,就會調用alarm函數的程序發送一個SIGALRM的函數的程序發送一個信号SIGALRM的信号。終止調用alarm函數的程序。

@當之前沒有設定過定時器,或者設定過的定時器已過時,則傳回0;

@當之前設定過,還沒有過時,則傳回剩餘的秒數。

@當alarm的參數為0時,取消一個定時器,傳回剩餘的秒數。

5、定時等待IO。

 @當在等待io時可以用alarm用設定定時阻塞。

 6、挂起程序。

@程序有三個狀态:運作;就緒和阻塞。

@當一個程序 在就緒态,但是我想他進入阻塞态時。可以調用。pause函數。

#include<unistd.h>

int pause(void);

直到有一個信号到來,并且執行了一個信号處理程式從其傳回後,pause函數傳回-1,表示執行正确。挂起後不再響應SIGTERM等很多信号。唯一能夠保證用kill.

7、程序休眠。

@pause使程序無時間限制的挂起,若想在一定時間恢複運作則使用sleep.

    unsigned int sleep(unsigned int nsec);

 傳回值有兩種:當挂起的時間超過了指定時間,傳回0;當時間還沒有到就被信号喚醒,還傳回挂起來的時間。

四、信号集與屏蔽信号。

1、信号集和信号處理函數。

@程序能夠捕捉并且處理的信号集合成為信号集。

@#include<signal.h>

 int sigemptyset(sigset_t *set):清空set,設定為0;

int sigfillset(sigset_t *set);設定為1;

int sigaddset(sigset_t* set, int signo);指定信号編号設定為1

int sigdelset(sigset_t * set ,int signo);指定信号編号設定為0

@使用sigismember函數測某個信号所對應的位是否被設定,

int sigismember(sigset_t *set,int signo);

被設定傳回1,未設定傳回0,失敗則傳回-1.

@信号的編号是由1開始的,但是信号集是從0開始的。signo的值應該是信号編碼減去1;

2、屏蔽信号。

@信号的屏蔽就是阻塞一個信号 。信号屏蔽字的本質同信号集一樣,是一個位向量。信号編碼對應的位為1表示屏蔽該信号,對應的位為0表示處理該信号。

@Linux環境sigprocmask函數設定信号屏蔽字,其函數原型如下:

#include <signal.h>

int sigprocask(int how,cosnt sigset_t *restrict set, sigset_t* restrict oset)

第二個參數set是一個信号集。

第三個參數oset被設定為原來的信号屏蔽字。

第一個參數how有三種取值情況。

SIG_BLOCK:set包含了希望添加到目前屏蔽信号集的信号。相當于mask=mask | set;

SIG_UNBLOCK:set包含了希望從目前屏蔽信号中解除阻塞的信号,相當于mask=mask & ~ set;

SIG_SETMASK:設定目前屏蔽信号集為set所指向的值,相當于mask=set; 

如果set的值是NULL,則無論how是何值都不會更改信号屏蔽字。這種方法用于得到目前程序的信号屏蔽字。

如: sigset_t oset;

             sigprocmask(0,NULL,&oset);//信号屏蔽字儲存在oset中。如oset為NULL,則表示忽略原來信号屏蔽字。成功就傳回0,否則傳回-1;

@注意SIGKILL和SIGSTOP這兩個信号不能被屏蔽。

3、處理未決信号。

@當屏蔽一個信号,但是程序在某處還是接收到此信号。這種信号叫做未決信号。

int sigpending (sigset_t *set);

參數表示為目前程序所有未決的信号 。

4、進階信号注冊函數。

  int sigaction(int signo,const struct sigaction *restrict act,struct sigaction *restict oact);

第一人參數表示注冊 的信号編号。

後面的參數表示act指向的結構設定信号處理函數,将原來的值儲存在oact中。

struct sigation{

      void (*sa_handler) (int);//信号處理函數

      sigset_t sa_mask;屏蔽信号集

    int sa_flags;信号選項

   void (*sa_sigaction) (int siginfo_t *,void *);替代sa_hanler的信号處理函數。

繼續閱讀