Linux訊號 二 訊號處理函式註冊
阿新 • • 發佈:2018-12-20
每一個訊號都有一個訊號處理函式,可以是SIG_IGN, SIG_DFL或者是使用者自定義的處理函式。使用使用者自定義的處理函式需要註冊,註冊介面有如下兩種。
第一種是signal呼叫
#include <signal.h> /** * sighandler_t是GNU的擴充套件,如果在glibc下面使用的話,編譯的時候需要加上-D_GNU_SOURCE * 或者手動定義 */ typedef void (*sighandler_t)(int); /** * 為訊號signum註冊訊號處理函式handler * 成功返回該訊號之前的處理函式,失敗返回SIG_ERR並將失敗原因填寫到errno中 */ sighandler_t signal(int signum, sighandler_t handler);
使用signal呼叫會有相容性問題,尤其是移植到其它UNIX系統上,所以推薦使用第二種訊號註冊函式sigaction,該函式功能相對signal而言,能夠提供更多功能。
#include <signal.h> /** * 註冊訊號處理函式,成功返回0,失敗返回-1並置errno * 引數act儲存待註冊的訊號處理函式結構體 * 如果oldact非空的話,舊的訊號處理函式會儲存到該結構體中 */ int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }; 該結構在註冊訊號處理函式sigaction中使用 1. sa_handler是一個引數為訊號值的處理函式 2. sa_sigaction也是一個訊號處理函式,不過它有三個引數,能夠獲取到處訊號值以外更多 資訊,當sa_flags中包含SA_SIGINFO標誌位的時候需要用到該函式。 3. sa_mask是訊號處理函式執行期間的遮蔽訊號集。就是說在訊號處理函式執行期間,遮蔽某 些訊號。但是不是所有訊號都能夠被遮蔽,SIGKILL和SIGSTOP這兩個訊號就無法屏 蔽,因為作業系統自身要能夠控制住程序。 4. sa_flags可以是下面這些值的集合: 1. SA_NOCLDSTOP, 這個標誌位只用於SIGCHLD訊號。父程序可以檢測子程序三個事件,子程序終止、 子程序停止、子程序回覆。SA_NOCLDSTOP標誌位用於控制後兩個事件。即一旦父程序 為SIGCHLD訊號設定了這個標誌位,那麼子程序停止和子程序恢復這兩件事情,就無需 向父程序傳送SIGCHLD訊號 2. SA_NOCLDWAIT 這個標誌只用於SIGCHLD訊號,它可控制子程序終止時候的行為,如果父程序 為SIGCHLD設定了SA_NOCLDWAIT標誌位,那麼子程序終止退出時,就不會進入殭屍 狀態,而是直接自行了斷。但是對Linux而言,子程序仍然會發送SIGCHLD訊號,這 點和上面的SA_NOCLDSTOP略有不同。 3. SA_ONESHOT和SA_RESETHAND 這兩個標誌位本質是一樣的,表示訊號處理函式是一次性的,訊號遞送出去以後,訊號 處理函式便恢復成預設值SIG_DFL. 4. SA_NODEFER和SA_NOMASK 這兩個標誌位的作用是一樣的,訊號處理函式執行期間,不阻塞當前訊號。 5. SA_RESTART 這個標誌位表示,如果系統呼叫被訊號中斷,則不返回錯誤,而是自動重啟系統呼叫。 6. SA_SIGINFO 這個標誌位表示訊號傳送者會提供額外的資訊。這種情況下,訊號處理函式應該為 三引數的函式。
當sa_flags含有SA_SIGINFO的時候 ,需要使用帶三個引數的處理函式:
void handler(int sig, siginfo_t *info, void *ucontext) { ... } 第一個引數 sig 為訊號值 第三個引數 ucontext,該結構體提供了程序上下文資訊,通常都不會使用到該引數,具體細節 可參考man sigreturn 第二個引數 info 是一個siginfo_t型別的指標,包含了訊號更多的資訊。該結構體如下: siginfo_t { int si_signo; /* 訊號值 */ int si_errno; /* An errno value */ int si_code; /* 訊號來源,可以通過該值來判斷訊號來源 * 可選值及含義 * SI_USER : 呼叫kill 或 raise的使用者程序 * SI_TKILL :呼叫tkill或tgkill的使用者程序 * SI_QUEUE : 呼叫sigqueue的使用者程序 * SI_MESGQ : 訊息到達POSIX訊息佇列 * SI_KERNEL : 核心產生的訊號 * SI_ASYNCIO : 非同步I/O操作完成 * SI_TIMER: POSIX定時器到期 */ int si_trapno; /* Trap number that caused hardware-generated signal (unused on most architectures) */ pid_t si_pid; /* 訊號傳送程序ID */ uid_t si_uid; /* 訊號傳送程序這是使用者ID */ 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; /* 使用sigqueue函式傳送訊號時攜帶的伴隨資料 */ 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 */ long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */ int si_fd; /* File descriptor */ short si_addr_lsb; /* Least significant bit of address (since Linux 2.6.32) */ void *si_lower; /* Lower bound when address violation occurred (since Linux 3.19) */ void *si_upper; /* Upper bound when address violation occurred (since Linux 3.19) */ int si_pkey; /* Protection key on PTE that caused fault (since Linux 4.6) */ void *si_call_addr; /* Address of system call instruction (since Linux 3.5) */ int si_syscall; /* Number of attempted system call (since Linux 3.5) */ unsigned int si_arch; /* Architecture of attempted system call (since Linux 3.5) */ } 上面的sigval_t結構體定義如下: union sigval { int sival_int; void *sival_ptr; } 通過指定sigqueue函式的第三個引數,可以傳遞給一個int值或者指標值個目標程序。考慮 到不同的程序有各自獨立的地址空間,傳遞指標到另一個程序幾乎沒有意義。