有些亂。。。湊合看把
0. 可靠信號機制原理:
0.0 當一個信號的出現時, 我們說信號"觸發"了.
0.1 我們可以爲一個進程指定某個信號相應的 handler.
有三種特殊的handler : SIG_DFL SIG_IGN SIG_ERR 分別代表: 默認, 忽略, 報錯 這三種handler
0.2 當進程收到信號並執行完相應 handler 之後意味着信號已經"發送完畢".
0.3 信號處於"觸發"後到"發送完畢"之前的時期, 叫做pending .
0.4 對於某個信號,我們可以進行 block 和 unblock 操作, 注意這跟忽略該信號是不一樣的.
0.5 在進程 block 某個信號之後, 該進程的這個信號可以由進程自身或其他進程"觸發"一次或多次, 在實時信號被觸發多次的情況下, 這些實時信號在內核維護下形成一個多級優先隊列按先後順序轉發至接受進程:
0.5.1 按照信號的 signal number 大小排序:
小的優先級高排前面, 大的優先級低排後面.
0.5.2 其次按照信號產生的時間先後對具有相同 signal number 的多個不同信號排序:
先產生的在前面, 後產生的在後面.
0.5.3 但同一時間某一進程既有傳統信號又有實時信號時, 他們由內核發送的順序是未定的.
0.6 一個信號一旦產生, 除非目標進程終止或退出之前一直被block, 否則遲早被接受.不存在傳統 UNIX 信號系統的不可靠的缺點.
0.7 可以通過調用 sigqueue() 觸發信號, 同時還可以在首發進程之間傳遞一個整型數或一個指針
0.8 注意信號 handler 可以中斷系統調用和庫函數調用, 而且可以在是內核態中斷.
1. 數據結構:
1.1 信號集合: bit vector, 每一個位對應一個信號是否 enable
# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;
typedef __sigset_t sigset_t;
1.2 信號集合操作:
#include <signal.h>
sigset_t set;
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
返回值:
除sigismember外:
成功: 0
出錯: -1
sigismember:
是 : 1
否 : 0
出錯: -1
1.2.1 sigset_t 類型應該在初次使用之前調用sigemptyset() 或 sigfillset() 先初始化.
1.3 操作進程當前使用信號集合 ( 進程忽略在 mask 集合中的信號 )
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how: {SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK}
返回值:
成功: 0
出錯: -1
1.4 爲特定信號綁定 handler
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
返回值:
成功: 0
出錯: -1
1.4.1 數據結構:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int , siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
注意:
1.4.1.0 signum 爲除SIGKILL和SIGSTOP外的所有信號.
1.4.1.1 不可同時指定元素sa_handler 和sa_sigaction(某些架構機器上, 這倆者屬於一個union ).
1.4.1.2 sa_handler 可爲宏 SIG_DFL:綁定該信號的系統默認handler, SIG_IGN: 忽落該信號.
1.4.1.3 sa_restorer 現已過時, 不應再使用. POSIX 中並未定義此元素.
1.4.1.4 可通過第2個參數爲NULL來查詢當前使用的 handler.
1.4.1.5 可通過指定第2和第3個參數爲NULL來校驗指定的信號在該機器上是否可以使用.
1.4.1.6 sa_mask 指定在handler處理信號期間,需要block的信號集合.此外觸發該handler 的
信號將在handler處理信號期間被屏蔽, 除非在sa_flags中使用了SA_NODEFER 標誌.
注意不可屏蔽
1.4.1.7 當在元素sa_flags中使用了 SA_SIGINFO 時, 註冊 sa_sigaction 爲信號處理 handler,
而不是 sa_handler
sa_sigaction 使用三個參數:
第1個參數,整型, 該信號的整數值;
第2個參數, siginfo_t 指針類型, ;
第3個參數,ucontext_t(轉換爲void 指針)類型.
注意: siginfo_t 包含許多可能的屬性,需要針對不同的信號選取對其有意義的屬性.
屬性如下:
siginfo_t {
/* 對於所有類型信號: */
int si_signo; /* Signal number */
int si_errno; /* An errno value ( Linux 中通常未使用此屬性 )*/
int si_code; /* Signal 的整型值, 而非 bit mask , 表示發生信號的原因 */
/*見 1.4.1.8 描述可能的取值, 以及相應的原因 */
union /* 針對不同的信號產生方式, 傳遞不同信息給 sa_sigaction */
{
int _pad[__SI_PAD_SIZE];
/* 進程調用 kill() 發送. */
struct
{
__pid_t si_pid; /* Sending process ID. */
__uid_t si_uid; /* Real user ID of sending process. */
} _kill;
/*計時器 timers (POSIX.1b) . */
struct
{
int si_tid; /* Timer ID. */
int si_overrun; /* Timer Overrun count. */
sigval_t si_sigval; /* Signal value. */
} _timer;
/*實時信號 (POSIX.1b). */
struct
{
__pid_t si_pid; /* Sending process ID. */
__uid_t si_uid; /* Real user ID of sending process. */
sigval_t si_sigval; /* 發送信號的進程傳來的數據. 參考 sigqueue(3p) */
/* 子類型: */
/* typedef union sigval */
/* { */
/* int sival_int; // */
/* void *sival_ptr; // */
/* } sigval_t; */
} _rt;
/* SIGCHLD(通常由子進程退出引起) */
struct
{
__pid_t si_pid; /* Which child. */
__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 */
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */
struct
{
void *si_addr; /* Memory location which caused fault */
} _sigfault;
/* SIGPOLL. */
struct
{
long int si_band; /* Band event for SIGPOLL. */
int si_fd; /* File descriptor */
} _sigpoll;
} _sifields;
}
1.4.1.8 關於siginfo_t 類型中 si_code 可取的值, 以及原因.
下面是對於一個任何類型的信號, si_code 可取的值及產生該信號的原因:
SI_USER kill(2) or raise(3)
SI_KERNEL Sent by the kernel.
SI_QUEUE sigqueue(2)
SI_TIMER POSIX timer expired
SI_MESGQ POSIX 消息隊列狀態改變 (since Linux 2.6.6); see mq_notify(3)
SI_ASYNCIO AIO 完成
SI_SIGIO queued SIGIO
SI_TKILL tkill(2) or tgkill(2) (since Linux 2.4.19)
下面是對於一個 SIGCHLD 信號, si_code 中可取的值和原因:
CLD_EXITED child has exited
CLD_KILLED child was killed
CLD_DUMPED child terminated abnormally
CLD_TRAPPED traced child has trapped
CLD_STOPPED child has stopped
CLD_CONTINUED stopped child has continued (since Linux 2.6.9)
下面是對於一個 SIGPOLL 信號, si_code 中可取的值和原因:
POLL_IN data input available
POLL_OUT output buffers available
POLL_MSG input message available
POLL_ERR i/o error
POLL_PRI high priority input available
POLL_HUP device disconnected
下面是對於一個 SIGILL 信號, si_code 中可取的值和原因:
ILL_ILLOPC illegal opcode
ILL_ILLOPN illegal operand
ILL_ILLADR illegal addressing mode
ILL_ILLTRP illegal trap
ILL_PRVOPC privileged opcode
ILL_PRVREG privileged register
ILL_COPROC coprocessor error
ILL_BADSTK internal stack error
下面是對於一個 SIGFPE 信號, si_code 中可取的值和原因:
FPE_INTDIV integer divide by zero
FPE_INTOVF integer overflow/
FPE_FLTDIV floating-point divide by zero
FPE_FLTOVF floating-point overflow
FPE_FLTUND floating-point underflow
FPE_FLTRES floating-point inexact result
FPE_FLTINV floating-point invalid operation
FPE_FLTSUB subscript out of range
下面是對於一個 SIGSEGV 信號, si_code 中可取的值和原因:
SEGV_MAPERR address not mapped to object
SEGV_ACCERR invalid permissions for mapped object
下面是對於一個 SIGBUS 信號, si_code 中可取的值和原因:
BUS_ADRALN invalid address alignment
BUS_ADRERR nonexistent physical address
BUS_OBJERR object-specific hardware error
下面是對於一個 SIGTRAP 信號, si_code 中可取的值和原因:
TRAP_BRKPT process breakpoint
TRAP_TRACE process trace trap
1.4.2 注意事項:
根據POSIX標準, 進程在忽略不是由 kill(2) 或raise(3) 產生的 SIGFPE, SIGILL, 或 SIGSEGV
信號時, 其行爲是未定義的 . 整數除零將產生未定義結果. 在某些架構機器上將產生一個
SIGFPE 信號 (同樣大多負整型數除 -1 也可能產生 SIGFPE.)忽略該信號可能導致死循環.
1.5 發送信號:
int sigqueue(pid_t pid, int signo, const union sigval value);
成功: 0
出錯: -1
1.5.1 sigqueue 函數會立即返回.
1.5.1.1如果 pid 進程調用sigaction函數爲 signo 指定的信號綁定handler 時使用了
SA_SIGINFO 標誌,並且系統有相應的資源, 則該信號將加入系統的信號隊列中, 並可
以被pid 號進程訪問.
1.5.1.2若未使用 SA_SIGINFO 標誌, 則至少向 pid 號進程發送一次 signo 信號. 而且此時對
於是否會向 pid 進程發送 value 參數是未定的.
1.5.1.3 注意:
在多線程環境中, ?一個線程調用 sigqueue 向所屬進程發送信號,
並且該線程中並未block該信號,
還有同一進程內也沒有其他的線程要處理這個信號(其它線程block掉了該信號, 或雖
然沒有block該信號,但也沒有使用 sigwait()來等待該信號),
則:
在本次 sigqueue 調用返回前, 就會向調用線程發送該信號.
1.6 檢查是否有 pending 的信號
int sigpending(sigset_t *set);
成功: 返回0 , 並將 pending 信號集合的 mask 寫入地址 set 中.
出錯: -1
1.6.1 因爲 set 爲 mask ,而不是直接的集合.
所以, 在判斷一個具體的信號是否有pending 的信號時應該如下使用,以SIGSTOP 爲例:
sigset_t set;
Hsigemptyset(&set);
Hsigpending(&set);
if( !sigismember( &set, signum) ) printf("There is pending SIGSTOP signals" );
1.7 等待信號, 掛起線程:
int sigwait(const sigset_t *set, int *sig);
返回值:
成功: 0 .
出錯:大於零的一個錯誤值.
1.7.1 掛起調用線程並等待, 直到收到一個在指定集合 set 中的信號時返回, 同時從pending 的
信號?列表中移除本次收到的這個信號, 並返回該信號數值到第二個參數 sig 中.
1.7.2 sigwait() 和 sigwaitinfo() 的區別在於:
sigwait()只根據 signal number 來識別需要等待的信號並返回本次信號的signal number.
sigwaitinfo() 則可以根據 siginfo_t 來識別需要等待的信號並返回本次信號的siginfo_t.
就是說 sigwaitinfo() 可以精確的指定需要等待的信號類型, 但也更加重量級
1.8 等待信號, 掛起調用進程:
int sigwaitinfo(const sigset_t *set, siginfo_t *info);
int sigtimedwait(const sigset_t *set, siginfo_t *info,
const struct timespec *timeout);
返回值:
成功: 本次信號的 signal number (大於零). 並從pending 信號列表中移除本次信號.
出錯: -1.
而且當sigtimedwait() 在設定時間內未接受到信號會設置 errono 爲 EAGAIN .
1.8.1 sigtimedwait() 除了多使用了一個超時參數 timeout 外, 其他都和sigwaitinfo 一樣.
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
}
當參數timeout 中兩個參數都指定爲 0 時,進行 poll 操作, 即 sigtimedwait()立即返回,並且:
在檢查信號queue之前, 其中已有要等待的信號, 立即返回該信號的 siginto_t 信息.
queue 中一個也沒有所要等待的信號時, 立即返回error.
1.9 等待信號, 掛起調用進程,:
int sigsuspend(const sigset_t *mask);
返回值:
成功: -1, 通常 errno 還被置爲 EINTR .
若該進程掛起後, 接受的是終止類型的信號則該進程被終止.
接受的是非終止類型的信號, 則該進程執行相應的handler.
所以這個函數返回值很怪異.
出錯: errno 被置爲 EFAULT ( 參考 sigsuspend(3p) );
1.8.1 注意:
通常 sigsuspend 會和sigprocmask 一起使用. 這樣可以預防在程序的關鍵代碼段執行期
間受某些信號影響:
1.8.1.1 首先進程在執行關鍵代碼之前調用sigprocmask 來blocks 某些信號. 並且將
執行block 操作之前的信號集合保存到 sigprocmask 的 oldset 中.
1.8.1.2 其次在關鍵代碼執行完後, 該進程調用 sigsuspend 並傳遞之前調用
sigprocmask時保存在它的第三個參數 oldset 中的信號集合給 sigsuspend.