訊號signal,為os傳遞給proc的一種事件通知。每種訊號對應某種系統event。訊號傳遞給proc,proc將會在核心態切換回使用者態時捕捉,並執行。具體執行機製為核心態跳轉到訊號對應的處理函數,訊號處理函數執行完畢之後切換回使用者態,故而在訊號處理函數執行的同時,proc本身的任務將處於休眠狀態,不會運行。在使用者態,proc不會捕捉處理訊號。此處在實際編程中需要注意。
Linux系統中訊號分為兩類,不可靠訊號與可靠訊號。訊號值小於SIGRTMIN(32)的訊號均為不可靠訊號,在SIGRTMIN(32)與SIGRTMAX(64)之間的為可靠訊號。可靠與不可靠主要針對於訊號丟失來說。(很多地方還有一條是訊號處理之後將會變成預設,此條現在已經不再有意義,因均不需要重新安裝。)可靠訊號支援訊號隊列,不會發生訊號丟失,而不可靠訊號不支援隊列。具體關於這方面的介紹可以google下,查看相關文檔即可。
訊號編程的步驟主要分為:
1、訊號捕捉進程:
訊號安裝
實現訊號處理函數,如果採取的方式為捕捉
2、訊號發送進程:
訊號發送
應用程式對於訊號的處理主要分為三種模式:忽略,預設,捕捉。
訊號的發送函數:
kill()
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int signo)
備忘:pid>0 進程為pid的proc
pid=0 同屬一個pg的proc
pid=-1 除發送進程以外的所有進程id大於1的進程
pid<0 && pid!=-1 pgid為pid的所有proc
signo為訊號值,當其取值為0時,不發送訊號,僅做例行檢查。主要檢查目標進程是否存在以及當前進程是否具有向目標進程發送訊號的許可權
調用成功時返回0,否則返回-1
raise()
#include <signal.h>
int raise(int signo)
向當前進程發送訊號,調用成功返回0,否則返回-1。
sigqueue()
#include <sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval val)
typedef union sigval {
int sival_int;
void *sival_ptr
}sigval_t;
該函數為新增的訊號安裝函數。其相對kill而言,多了一個參數val,用於傳遞附加資訊給訊號處理函數。其缺點在於僅能對一個進程發送訊號,如果sig為零,其工作機制與kill一致,均為例行檢查。
alarm()
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
專門為SIGALARM訊號而設定的定時器,即在指定seconds之後觸發,向當前進程本身發送SIGALARM訊號。進程調用該方法之後,直接覆蓋之前的調用,即多次調用僅有最後一次有效,傳回值與之前是否調用過該方法有關,如果之前設定過鬧鐘時間,則返回之前的鬧鐘時間的剩餘時間,否則,返回0。鬧鐘時間觸發處理之後,該設定將會被自動取消,故而當需要多次調用時,需要處理完畢之後重新設定鬧鐘時間。該函數目前已經很少使用。
setitimer()
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
功能與alarm類似,但相對強大很多。其工作機製為value自動遞減其成員it_value,至零,觸發訊號SIGALARM,然後用it_interval填充it_value繼續周期性執行。當it_interval與it_value均為零時,則定時器取消。故而如果設定一次性定時器,僅需設定其中it_value取值,而將it_interval設定為零,如果需要設定周期性定時器,則必須將it_interval設定值。
其參數which的取值有三種:ITIMER_REAL 設定絕對時間,對應訊號SIGALARM, ITIMER_VIRTUAL 程式執行時間,對應訊號SIGVALARM,ITIMER_PROF 進程執行以及核心因本進程消耗的時間和,對應訊號SIGVALARM。
abort()
#include <stdlib.h>
void abort(void);
向進程本身發送SIGABORT訊號。進程調用其對應的處理函數之後仍能接受SIGABORT訊號。
訊號安裝:
signal()
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
參數signum為訊號值,handler為處理函數控制代碼。如果忽略,使用SIG_IGN,如果預設則使用SIG_DFL,如果捕捉,則此處需要使用自訂的處理函數的入口地址。調用成功返回處理函數控制代碼,否則返回SIG_ERR。
sigaction()
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
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);
}
signum對應需要安裝的訊號,act的成員sa_handler與_sa_sigaction對應處理函數的入口地址。其中sa_handler與_sa_sigaction僅可指定一個。其中_sa_handler僅存在一個參數,為訊號值。如果使用函數_sa_sigaction,其存在三個參數,第三個參數無效。第一個參數為訊號值,第二個參數為一個結構體。其結構如下:
struct siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
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 */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count; POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}
其中void *si_ptr; 為4位元組,對應之前訊號發送函數sigqueue的第三個參數。此項在實際應用中具有很好的擴充性。
sa_mask標識在該訊號處理函數執行過程中,那些訊號將被屏蔽。預設情況下為當前訊號本身被屏蔽,以防止訊號的嵌套發送。訊號集處理函數包括:
#include <signal.h>
int sigemptyset(sigset_t *set);//初始化由set指定的訊號集,訊號集裡面的所有訊號被清空;
int sigfillset(sigset_t *set);//調用該函數後,set指向的訊號集中將包含linux支援的64種訊號;
int sigaddset(sigset_t *set, int signum);//在set指向的訊號集中加入signum訊號;
int sigdelset(sigset_t *set, int signum);//在set指向的訊號集中刪除signum訊號;
int sigismember(const sigset_t *set, int signum);)//判定訊號signum是否在set指向的訊號集中。
sa_flags中包含了許多標誌位,包括剛剛提到的SA_NODEFER及SA_NOMASK標誌位。另一個比較重要的標誌位是SA_SIGINFO,當設定了該標誌位時,表示訊號附帶的參數可以被傳遞到訊號處理函數中,因此,應該為sigaction結構中的sa_sigaction指定處理函數,而不應該為sa_handler指定訊號處理函數,否則,設定該標誌變得毫無意義。即使為sa_sigaction指定了訊號處理函數,如果不設定SA_SIGINFO,訊號處理函數同樣不能得到訊號傳遞過來的資料,在訊號處理函數中對這些資訊的訪問都將導致段錯誤。
進程相關訊號操作函數
//參數how的說明:
//SIG_BLOCK 在進程當前阻塞訊號集中添加set指向訊號集中的訊號
//SIG_UNBLOCK 如果進程阻塞訊號集中包含set指向訊號集中的訊號,則解除對該訊號的阻塞
//SIG_SETMASK 更新進程阻塞訊號集為set指向的訊號集
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));
int sigpending(sigset_t *set));//獲得當前已遞送到進程,卻被阻塞的所有訊號,在set指向的訊號集中返回結果。
int sigsuspend(const sigset_t *mask));//用於在接收到某個訊號之前, 臨時用mask替換進程的訊號掩碼, 並暫停進程執行,直到收到訊號為止。sigsuspend 返回後將恢複調用之前的訊號掩碼。訊號處理函數完成後,進程將繼續執行。該系統調用始終返回-1,並將errno設定為EINTR。
void pause(void);進程阻塞等待,直到有訊號喚醒。