訊號之sigaction函式
igaction函式的功能是檢查或修改與指定訊號相關聯的處理動作(或同時執行這兩種操作)。
#include <signal.h>
int sigaction( int signo, const struct sigaction *restrict act, struct sigaction *restrict oact); 返回值:若成功則返回0,若出錯則返回-1
其中,引數signo是要檢測或修改其具體動作的訊號編號。若act指標非空,則要修改其動作。如果oact指標非空,則系統經由oact指標返回該訊號的上一個動作。此函式使用下列結構:
struct sigaction {
void (*sa_handler)(int); /* addr of signal handler, or SIG_IGN, or SIG_DFL */ sigset_t sa_mask; /* additional signals to block */ int sa_flags; /* signal options */ /* alternate handler */ void (*sa_sigaction)(int, siginfo_t *, void *); };
當更改訊號動作時,如果sa_handler欄位包含一個訊號捕捉函式的地址(與常量SIG_IGN或SIG_DFL相對),則sa_mask欄位說明了一個訊號集,在呼叫該訊號捕捉函式之前,這一訊號集要加到程序的訊號遮蔽字中。僅當從訊號捕捉函式返回時再將程序的訊號遮蔽字復位為原先值。這樣,在呼叫訊號處理程式時就能阻塞某些訊號。在訊號處理程式被呼叫時,作業系統建立的新訊號遮蔽字包括正被遞送的訊號。因此保證了在處理一個給定的訊號時,如果這種訊號再次發生,那麼它會被阻塞到對前一個訊號的處理結束為止。
一旦對給定的訊號設定了一個動作,那麼在呼叫sigaction顯式地改變它之前,該設定就一直有效
act結構的sa_flags欄位指定對訊號進行處理的各個選項。
表10-5 處理每個訊號的選項標誌(sa_flags)
sa_sigaction欄位是一個替代的訊號處理程式,當在sigaction結構中使用了SA_SIGINFO標誌時,使用該訊號處理程式。對於sa_sigaction欄位和sa_handler欄位這兩者,其實現可能使用同一儲存區,所以應用程式只能一次使用這兩個欄位中的一個。
通常,按下列方式呼叫訊號處理程式:
void handler(int signo);
但是,如果設定了SA_SIGINFO標誌,那麼按下列方式呼叫訊號處理程式:
void handler(int signo, siginfo_t *info, void *context);
siginfo_t結構包含了訊號產生原因的有關資訊。該結構的大致樣式如下所示:
struct siginfo {
int si_signo; /* signal number */ int si_errno; /* if nonzero, errno value from <errno.h> */ int si_code; /* additional info (depends on signal) */ pid_t si_pid; /* sending process ID */ uid_t si_uid; /* sending process real user ID */ void *si_addr; /* address that caused the fault */ int si_status; /* exit value or signal number */ long si_band; /* band number for SIGPOLL */ /* possibly other fields also */ };
各種訊號的si_code值(包括上面的相關資料結構和標誌選項),可通過man sigaction命令進行檢視。
若訊號是SIGCHLD,則將設定si_pid、si_status和si_uid欄位。
若訊號是SIGILL或SIGSEGV,則si_addr包含造成故障的根源地址,儘管該地址可能並不準確。
若訊號是SIGPOLL,那麼si_band欄位將包含STREAMS訊息的優先順序(priority band),該訊息產生POLL_IN、POLL_OUT或POLL_MSG事件。
si_errno欄位包含錯誤編號,它對應於引發訊號產生的條件,並由實現定義。
訊號處理程式的context引數是無型別指標,它可被強制轉換為ucntext_t結構型別,用於標識訊號傳遞時程序的上下文。
例項:signal函式
現在用sigaction實現signal函式。很多平臺都是這樣做的。
程式清單10-12 用sigaction實現signal函式
#include "apue.h"
/* Reliable version of signal(), using POSIX sigaction(). */ Sigfunc * signal(int signo, Sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(signo == SIGALRM) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif } if(sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); }
注意,必須用sigemptyset函式初始化act結構的sa_mask成員。不能保證:act.sa_mask = 0;會做同樣的事情。
對除SIGALRM以外的所有訊號,我們都有嘗試設定SA_RESTART標誌,於是被這些訊號中斷的系統呼叫都能自動重啟動。不希望重啟動由SIGALRM訊號中斷的系統呼叫的原因是:我們希望對I/O操作可以設定時間限制。
例項:signal_intr函式
程式清單10-13是signal函式的另一種版本,它力圖阻止任何被中斷的系統呼叫重啟動。
程式清單10-13 signal_intr函式
#include "apue.h"
Sigfunc *
signal_intr(int signo, Sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif if(sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); }
如果系統定義了SA_INTERRUPT標誌,那麼為了提高可移植性,我們在sa_flags中增加該標誌,這樣也就阻止了被中斷的系統呼叫重啟動。