一 信號(hào)的種類
可靠信號(hào)與不可靠信號(hào), 實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)
可靠信號(hào)就是實(shí)時(shí)信號(hào), 那些從UNIX系統(tǒng)繼承過來的信號(hào)都是非可靠信號(hào), 表現(xiàn)在信號(hào)
不支持排隊(duì),信號(hào)可能會(huì)丟失, 比如發(fā)送多次相同的信號(hào), 進(jìn)程只能收到一次. 信號(hào)值小于
SIGRTMIN的都是非可靠信號(hào).
非可靠信號(hào)就是非實(shí)時(shí)信號(hào), 后來, Linux改進(jìn)了信號(hào)機(jī)制, 增加了32種新的信號(hào), 這些信
號(hào)都是可靠信號(hào), 表現(xiàn)在信號(hào)支持排隊(duì), 不會(huì)丟失, 發(fā)多少次, 就可以收到多少次. 信號(hào)值
位于 [SIGRTMIN, SIGRTMAX] 區(qū)間的都是可靠信號(hào).
關(guān)于可靠信號(hào), 還可以參考WIKI的一段話:
Text代碼

- The real-time signals, ranging from SIGRTMIN to SIGRTMAX, are a set of signals that can be used for application-defined purposes.
- Because SIGRTMIN may have different values on different Unix-like systems, applications should always refer to the signals in the form SIGRTMIN+n, where n is a constant integer expression.
- The real-time signals have a number of properties that differentiate them from other signals and make them suitable for application-defined purposes:
- * Multiple instances of a real-time signal can be sent to a process and all will be delivered.
- * Real-time signals can be accompanied by an integer or pointer value (see sigqueue[2]).
- * Real-time signals are guaranteed to be delivered in the order they were emitted.
命令行輸入 kill -l, 可以列出系統(tǒng)支持的所有信號(hào):
C代碼

- ~> kill -l
- 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
- 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
- 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
- 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
- 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
- 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
- 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
- 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
- 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
- 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
- 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
- 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
- 63) SIGRTMAX-1 64) SIGRTMAX
非可靠信號(hào)一般都有確定的用途及含義, 可靠信號(hào)則可以讓用戶自定義使用
二 信號(hào)的安裝
早期的Linux使用系統(tǒng)調(diào)用 signal 來安裝信號(hào)
#include <signal.h>
void (*signal(int signum, void (*handler))(int)))(int);
該函數(shù)有兩個(gè)參數(shù), signum指定要安裝的信號(hào), handler指定信號(hào)的處理函數(shù).
該函數(shù)的返回值是一個(gè)函數(shù)指針, 指向上次安裝的handler
經(jīng)典安裝方式:
if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
signal(SIGINT, sig_handler);
}
先獲得上次的handler, 如果不是忽略信號(hào), 就安裝此信號(hào)的handler
由于信號(hào)被交付后, 系統(tǒng)自動(dòng)的重置handler為默認(rèn)動(dòng)作, 為了使信號(hào)在handler
處理期間, 仍能對(duì)后繼信號(hào)做出反應(yīng), 往往在handler的第一條語句再次調(diào)用 signal
sig_handler(ing signum)
{
/* 重新安裝信號(hào) */
signal(signum, sig_handler);
......
}
我們知道在程序的任意執(zhí)行點(diǎn)上, 信號(hào)隨時(shí)可能發(fā)生, 如果信號(hào)在sig_handler重新安裝
信號(hào)之前產(chǎn)生, 這次信號(hào)就會(huì)執(zhí)行默認(rèn)動(dòng)作, 而不是sig_handler. 這種問題是不可預(yù)料的.
使用庫函數(shù) sigaction 來安裝信號(hào)
為了克服非可靠信號(hào)并同一SVR4和BSD之間的差異, 產(chǎn)生了 POSIX 信號(hào)安裝方式, 使用
sigaction安裝信號(hào)的動(dòng)作后, 該動(dòng)作就一直保持, 直到另一次調(diào)用 sigaction建立另一個(gè)
動(dòng)作為止. 這就克服了古老的 signal 調(diào)用存在的問題
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
經(jīng)典安裝方式:
struct sigaction action, old_action;
/* 設(shè)置SIGINT */
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGTERM);
action.sa_flags = 0;
/* 獲取上次的handler, 如果不是忽略動(dòng)作, 則安裝信號(hào) */
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT, &action, NULL);
}
基于 sigaction 實(shí)現(xiàn)的庫函數(shù): signal
sigaction 自然強(qiáng)大, 但安裝信號(hào)很繁瑣, 目前l(fā)inux中的signal()是通過sigation()函數(shù)
實(shí)現(xiàn)的,因此,即使通過signal()安裝的信號(hào),在信號(hào)處理函數(shù)的結(jié)尾也不必
再調(diào)用一次信號(hào)安裝函數(shù)。
三 如何屏蔽信號(hào)
所謂屏蔽, 并不是禁止遞送信號(hào), 而是暫時(shí)阻塞信號(hào)的遞送,
解除屏蔽后, 信號(hào)將被遞送, 不會(huì)丟失. 相關(guān)API為
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);
int sigsuspend(const sigset_t *mask);
int sigpending(sigset_t *set);
-----------------------------------------------------------------
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));
sigprocmask()函數(shù)能夠根據(jù)參數(shù)how來實(shí)現(xiàn)對(duì)信號(hào)集的操作,操作主要有三種:
* SIG_BLOCK 在進(jìn)程當(dāng)前阻塞信號(hào)集中添加set指向信號(hào)集中的信號(hào)
* SIG_UNBLOCK 如果進(jìn)程阻塞信號(hào)集中包含set指向信號(hào)集中的信號(hào),則解除
對(duì)該信號(hào)的阻塞
* SIG_SETMASK 更新進(jìn)程阻塞信號(hào)集為set指向的信號(hào)集
屏蔽整個(gè)進(jìn)程的信號(hào):
C代碼

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
void sig_handler(int signum)
{
printf("catch SIGINT\n");
}
int main(int argc, char **argv)
{
sigset_t block;
struct sigaction action, old_action;
/* 安裝信號(hào) */
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT, &action, NULL);
}
/* 屏蔽信號(hào) */
sigemptyset(&block);
sigaddset(&block, SIGINT);
printf("block SIGINT\n");
sigprocmask(SIG_BLOCK, &block, NULL);
printf("--> send SIGINT -->\n");
kill(getpid(), SIGINT);
printf("--> send SIGINT -->\n");
kill(getpid(), SIGINT);
sleep(1);
/* 解除信號(hào)后, 之前觸發(fā)的信號(hào)將被遞送,
* 但SIGINT是非可靠信號(hào), 只會(huì)遞送一次
*/
printf("unblock SIGINT\n");
sigprocmask(SIG_UNBLOCK, &block, NULL);
sleep(2);
return 0;
}
運(yùn)行結(jié)果:
C代碼

work> ./a.out
block SIGINT
--> send SIGINT -->
--> send SIGINT -->
unblock SIGINT
catch SIGINT
這里發(fā)送了兩次SIGINT信號(hào) 可以看到, 屏蔽掉SIGINT后,
信號(hào)無法遞送, 解除屏蔽后, 才遞送信號(hào), 但只被遞送一次,
因?yàn)镾IGINT是非可靠信號(hào), 不支持排隊(duì).
只在信號(hào)處理期間, 屏蔽其它信號(hào)
在信號(hào)的handler執(zhí)行期間, 系統(tǒng)將自動(dòng)屏蔽此信號(hào), 但如果
還想屏蔽其它信號(hào)怎么辦? 可以利用 struct sigaction 結(jié)構(gòu)體
的 sa_mask 屬性.
C代碼

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
void sig_handler(int signum)
{
printf("in handle, SIGTERM is blocked\n");
/* 在此handler內(nèi)將屏蔽掉SIGTERM, 直到此handler返回 */
printf("--> send SIGTERM -->\n");
kill(getpid(), SIGTERM);
sleep(5);
printf("handle done\n");
}
void handle_term(int signum)
{
printf("catch sigterm and exit..\n");
exit(0);
}
int main(int argc, char **argv)
{
struct sigaction action, old_action;
/* 設(shè)置SIGINT */
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
/* 安裝handler的時(shí)候, 設(shè)置在handler
* 執(zhí)行期間, 屏蔽掉SIGTERM信號(hào) */
sigaddset(&action.sa_mask, SIGTERM);
action.sa_flags = 0;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT, &action, NULL);
}
/* 設(shè)置SIGTERM */
action.sa_handler = handle_term;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGTERM, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGTERM, &action, NULL);
}
printf("--> send SIGINT -->\n");
kill(getpid(), SIGINT);
while (1) {
sleep(1);
}
return 0;
}
運(yùn)行結(jié)果:
C代碼

work> ./a.out
--> send SIGINT -->
in handle, SIGTERM is blocked
--> send SIGTERM -->
handle done
catch sigterm and exit..
收到SIGINT后, 進(jìn)入sig_handler,此時(shí)發(fā)送SIGTERM信號(hào)將被屏蔽,
等sig_handler返回后, 才收到SIGTERM信號(hào), 然后退出程序
四 如何獲取未決信號(hào)
所謂未決信號(hào), 是指被阻塞的信號(hào), 等待被遞送的信號(hào).
int sigsuspend(const sigset_t *mask));
sigpending(sigset_t *set))獲得當(dāng)前已遞送到進(jìn)程,
卻被阻塞的所有信號(hào),在set指向的信號(hào)集中返回結(jié)果。
C代碼

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
/* 版本1, 可靠信號(hào)將被遞送多次 */
//#define MYSIGNAL SIGRTMIN+5
/* 版本2, 不可靠信號(hào)只被遞送一次 */
#define MYSIGNAL SIGTERM
void sig_handler(int signum)
{
psignal(signum, "catch a signal");
}
int main(int argc, char **argv)
{
sigset_t block, pending;
int sig, flag;
/* 設(shè)置信號(hào)的handler */
signal(MYSIGNAL, sig_handler);
/* 屏蔽此信號(hào) */
sigemptyset(&block);
sigaddset(&block, MYSIGNAL);
printf("block signal\n");
sigprocmask(SIG_BLOCK, &block, NULL);
/* 發(fā)兩次信號(hào), 看信號(hào)將會(huì)被觸發(fā)多少次 */
printf("---> send a signal --->\n");
kill(getpid(), MYSIGNAL);
printf("---> send a signal --->\n");
kill(getpid(), MYSIGNAL);
/* 檢查當(dāng)前的未決信號(hào) */
flag = 0;
sigpending(&pending);
for (sig = 1; sig < NSIG; sig++) {
if (sigismember(&pending, sig)) {
flag = 1;
psignal(sig, "this signal is pending");
}
}
if (flag == 0) {
printf("no pending signal\n");
}
/* 解除此信號(hào)的屏蔽, 未決信號(hào)將被遞送 */
printf("unblock signal\n");
sigprocmask(SIG_UNBLOCK, &block, NULL);
/* 再次檢查未決信號(hào) */
flag = 0;
sigpending(&pending);
for (sig = 1; sig < NSIG; sig++) {
if (sigismember(&pending, sig)) {
flag = 1;
psignal(sig, "a pending signal");
}
}
if (flag == 0) {
printf("no pending signal\n");
}
return 0;
}
這個(gè)程序有兩個(gè)版本:
可靠信號(hào)版本, 運(yùn)行結(jié)果:
C代碼

work> ./a.out
block signal
---> send a signal --->
---> send a signal --->
this signal is pending: Unknown signal 39
unblock signal
catch a signal: Unknown signal 39
catch a signal: Unknown signal 39
no pending signal
發(fā)送兩次可靠信號(hào), 最終收到兩次信號(hào)
非可靠信號(hào)版本, 運(yùn)行結(jié)果:
C代碼

work> ./a.out
block signal
---> send a signal --->
---> send a signal --->
this signal is pending: Terminated
unblock signal
catch a signal: Terminated
no pending signal
發(fā)送兩次非可靠信號(hào), 最終只收到一次
五 被中斷了的系統(tǒng)調(diào)用
一些IO系統(tǒng)調(diào)用執(zhí)行時(shí), 如 read 等待輸入期間, 如果收到一個(gè)信號(hào),
系統(tǒng)將中斷read, 轉(zhuǎn)而執(zhí)行信號(hào)處理函數(shù). 當(dāng)信號(hào)處理返回后, 系統(tǒng)
遇到了一個(gè)問題: 是重新開始這個(gè)系統(tǒng)調(diào)用, 還是讓系統(tǒng)調(diào)用失敗?
早期UNIX系統(tǒng)的做法是, 中斷系統(tǒng)調(diào)用, 并讓系統(tǒng)調(diào)用失敗, 比如read
返回 -1, 同時(shí)設(shè)置 errno 為 EINTR
中斷了的系統(tǒng)調(diào)用是沒有完成的調(diào)用, 它的失敗是臨時(shí)性的, 如果再次調(diào)用
則可能成功, 這并不是真正的失敗, 所以要對(duì)這種情況進(jìn)行處理, 典型的方式為:
while (1) {
n = read(fd, buf, BUFSIZ);
if (n == -1 && errno != EINTR) {
printf("read error\n");
break;
}
if (n == 0) {
printf("read done\n");
break;
}
}
這樣做邏輯比較繁瑣, 事實(shí)上, 我們可以從信號(hào)的角度
來解決這個(gè)問題, 安裝信號(hào)的時(shí)候, 設(shè)置 SA_RESTART
屬性, 那么當(dāng)信號(hào)處理函數(shù)返回后, 被該信號(hào)中斷的系統(tǒng)
調(diào)用將自動(dòng)恢復(fù).
C代碼

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
void sig_handler(int signum)
{
printf("in handler\n");
sleep(1);
printf("handler return\n");
}
int main(int argc, char **argv)
{
char buf[100];
int ret;
struct sigaction action, old_action;
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
/* 版本1:不設(shè)置SA_RESTART屬性
* 版本2:設(shè)置SA_RESTART屬性 */
//action.sa_flags |= SA_RESTART;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT, &action, NULL);
}
bzero(buf, 100);
ret = read(0, buf, 100);
if (ret == -1) {
perror("read");
}
printf("read %d bytes:\n", ret);
printf("%s\n", buf);
return 0;
}
版本1, 不設(shè)置 SA_RESTART 屬性 :
C代碼

work> gcc signal.c
work> ./a.out
^Cin handler
handler return
read: Interrupted system call
read -1 bytes:
在 read 等待數(shù)據(jù)期間, 按下ctrl + c, 觸發(fā) SIGINT 信號(hào),
handler 返回后, read 被中斷, 返回 -1
版本2, 設(shè)置 SA_RESTART 屬性:
C代碼

work> gcc signal.c
work> ./a.out
^Cin handler
handler return
hello, world
read 13 bytes:
hello, world
handler 返回后, read 系統(tǒng)調(diào)用被恢復(fù)執(zhí)行, 繼續(xù)等待數(shù)據(jù).
六 非局部控制轉(zhuǎn)移
int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);
void longjmp(jmp_buf env, int val);
void siglongjmp(sigjmp_buf env, int val);
--------------------------------------------------------
setjmp()會(huì)保存目前堆棧環(huán)境,然后將目前的地址作一個(gè)記號(hào),
而在程序其他地方調(diào)用 longjmp 時(shí)便會(huì)直接跳到這個(gè)記號(hào)位置,
然后還原堆棧,繼續(xù)程序好執(zhí)行。
setjmp調(diào)用有點(diǎn)fork的味道, setjmp()return 0 if returning directly,
and non-zero when returning from longjmp using the saved context.
if (setjmp(jmpbuf)) {
printf("return from jmp\n");
} else {
printf("return directly\n");
}
setjmp 和 sigsetjmp 的唯一區(qū)別是: setjmp 不一定會(huì)恢復(fù)信號(hào)集合,
而sigsetjmp可以保證恢復(fù)信號(hào)集合
C代碼

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <setjmp.h>
void sig_alrm(int signum);
void sig_usr1(int signum);
void print_mask(const char *str);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjmp;
static int sigalrm_appear;
int main(int argc, char **argv)
{
struct sigaction action, old_action;
/* 設(shè)置SIGUSR1 */
action.sa_handler = sig_usr1;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGUSR1, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGUSR1, &action, NULL);
}
/* 設(shè)置SIGALRM */
action.sa_handler = sig_alrm;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGALRM, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGALRM, &action, NULL);
}
print_mask("starting main:");
if (sigsetjmp(jmpbuf, 1) != 0) {
print_mask("exiting main:");
} else {
printf("sigsetjmp return directly\n");
canjmp = 1;
while (1) {
sleep(1);
}
}
return 0;
}
void sig_usr1(int signum)
{
time_t starttime;
if (canjmp == 0) {
printf("please set jmp first\n");
return;
}
print_mask("in sig_usr1:");
alarm(1);
while (!sigalrm_appear);
canjmp = 0;
siglongjmp(jmpbuf, 1);
}
void sig_alrm(int signum)
{
print_mask("in sig_alrm:");
sigalrm_appear = 1;
return;
}
void print_mask(const char *str)
{
sigset_t sigset;
int i, errno_save, flag = 0;
errno_save = errno;
if (sigprocmask(0, NULL, &sigset) < 0) {
printf("sigprocmask error\n");
exit(0);
}
printf("%s\n", str);
fflush(stdout);
for (i = 1; i < NSIG; i++) {
if (sigismember(&sigset, i)) {
flag = 1;
psignal(i, "a blocked signal");
}
}
if (!flag) {
printf("no blocked signal\n");
}
printf("\n");
errno = errno_save;
}
運(yùn)行結(jié)果:
C代碼

work> ./a.out &
[4] 28483
starting main:
no blocked signal
sigsetjmp return directly
kill -USR1 28483
in sig_usr1:
a blocked signal: User defined signal 1
in sig_alrm:
a blocked signal: User defined signal 1
a blocked signal: Alarm clock
exiting main:
no blocked signal
七 信號(hào)的生命周期
從信號(hào)發(fā)送到信號(hào)處理函數(shù)的執(zhí)行完畢
對(duì)于一個(gè)完整的信號(hào)生命周期(從信號(hào)發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來說,
可以分為三個(gè)重要的階段,這三個(gè)階段由四個(gè)重要事件來刻畫:
信號(hào)誕生;信號(hào)在進(jìn)程中注冊(cè)完畢;信號(hào)在進(jìn)程中的注銷完畢;信號(hào)處理函數(shù)執(zhí)行完畢。
下面闡述四個(gè)事件的實(shí)際意義:
信號(hào)"誕生"。信號(hào)的誕生指的是觸發(fā)信號(hào)的事件發(fā)生
(如檢測(cè)到硬件異常、定時(shí)器超時(shí)以及調(diào)用信號(hào)發(fā)送函數(shù)
kill()或sigqueue()等)。信號(hào)在目標(biāo)進(jìn)程中"注冊(cè)";
進(jìn)程的task_struct結(jié)構(gòu)中有關(guān)于本進(jìn)程中未決信號(hào)的數(shù)據(jù)成員:
struct sigpending pending:
struct sigpending{
struct sigqueue *head, **tail;
sigset_t signal;
};
第三個(gè)成員是進(jìn)程中所有未決信號(hào)集,第一、第二個(gè)成員分別指向一個(gè)
sigqueue類型的結(jié)構(gòu)鏈(稱之為"未決信號(hào)鏈表")的首尾,鏈表中
的每個(gè)sigqueue結(jié)構(gòu)刻畫一個(gè)特定信號(hào)所攜帶的信息,并指向下一個(gè)
sigqueue結(jié)構(gòu):
struct sigqueue{
struct sigqueue *next;
siginfo_t info;
}
信號(hào)的注冊(cè)
信號(hào)在進(jìn)程中注冊(cè)指的就是信號(hào)值加入到進(jìn)程的未決信號(hào)集中
(sigpending結(jié)構(gòu)的第二個(gè)成員sigset_t signal),
并且加入未決信號(hào)鏈表的末尾。 只要信號(hào)在進(jìn)程的未決信號(hào)集中,
表明進(jìn)程已經(jīng)知道這些信號(hào)的存在,但還沒來得及處理,或者該信號(hào)被進(jìn)程阻塞。
當(dāng)一個(gè)實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),不管該信號(hào)是否已經(jīng)在進(jìn)程中注冊(cè),
都會(huì)被再注冊(cè)一次,因此,信號(hào)不會(huì)丟失,因此,實(shí)時(shí)信號(hào)又叫做"可靠信號(hào)"。
這意味著同一個(gè)實(shí)時(shí)信號(hào)可以在同一個(gè)進(jìn)程的未決信號(hào)鏈表中添加多次.
當(dāng)一個(gè)非實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),如果該信號(hào)已經(jīng)在進(jìn)程中注冊(cè),
則該信號(hào)將被丟棄,造成信號(hào)丟失。因此,非實(shí)時(shí)信號(hào)又叫做"不可靠信號(hào)"。
這意味著同一個(gè)非實(shí)時(shí)信號(hào)在進(jìn)程的未決信號(hào)鏈表中,至多占有一個(gè)sigqueue結(jié)構(gòu).
一個(gè)非實(shí)時(shí)信號(hào)誕生后,
(1)、如果發(fā)現(xiàn)相同的信號(hào)已經(jīng)在目標(biāo)結(jié)構(gòu)中注冊(cè),則不再注冊(cè),對(duì)于進(jìn)程來說,
相當(dāng)于不知道本次信號(hào)發(fā)生,信號(hào)丟失.
(2)、如果進(jìn)程的未決信號(hào)中沒有相同信號(hào),則在進(jìn)程中注冊(cè)自己。
信號(hào)的注銷。
在進(jìn)程執(zhí)行過程中,會(huì)檢測(cè)是否有信號(hào)等待處理
(每次從系統(tǒng)空間返回到用戶空間時(shí)都做這樣的檢查)。如果存在未決
信號(hào)等待處理且該信號(hào)沒有被進(jìn)程阻塞,則在運(yùn)行相應(yīng)的信號(hào)處理函數(shù)前,
進(jìn)程會(huì)把信號(hào)在未決信號(hào)鏈中占有的結(jié)構(gòu)卸掉。是否將信號(hào)從進(jìn)程未決信號(hào)集
中刪除對(duì)于實(shí)時(shí)與非實(shí)時(shí)信號(hào)是不同的。對(duì)于非實(shí)時(shí)信號(hào)來說,由于在未決信
號(hào)信息鏈中最多只占用一個(gè)sigqueue結(jié)構(gòu),因此該結(jié)構(gòu)被釋放后,應(yīng)該把信
號(hào)在進(jìn)程未決信號(hào)集中刪除(信號(hào)注銷完畢);而對(duì)于實(shí)時(shí)信號(hào)來說,可能在
未決信號(hào)信息鏈中占用多個(gè)sigqueue結(jié)構(gòu),因此應(yīng)該針對(duì)占用sigqueue結(jié)構(gòu)
的數(shù)目區(qū)別對(duì)待:如果只占用一個(gè)sigqueue結(jié)構(gòu)(進(jìn)程只收到該信號(hào)一次),
則應(yīng)該把信號(hào)在進(jìn)程的未決信號(hào)集中刪除(信號(hào)注銷完畢)。否則,不應(yīng)該在進(jìn)程
的未決信號(hào)集中刪除該信號(hào)(信號(hào)注銷完畢)。
進(jìn)程在執(zhí)行信號(hào)相應(yīng)處理函數(shù)之前,首先要把信號(hào)在進(jìn)程中注銷。
信號(hào)生命終止。
進(jìn)程注銷信號(hào)后,立即執(zhí)行相應(yīng)的信號(hào)處理函數(shù),執(zhí)行完畢后,
信號(hào)的本次發(fā)送對(duì)進(jìn)程的影響徹底結(jié)束。
八 關(guān)于可重入函數(shù)
在信號(hào)處理函數(shù)中應(yīng)使用可重入函數(shù)。
信號(hào)處理程序中應(yīng)當(dāng)使用可重入函數(shù)
(注:所謂可重入函數(shù)是指一個(gè)可以被多個(gè)任務(wù)調(diào)用的過程,
任務(wù)在調(diào)用時(shí)不必?fù)?dān)心數(shù)據(jù)是否會(huì)出錯(cuò))。因?yàn)檫M(jìn)程在收到信號(hào)
后,就將跳轉(zhuǎn)到信號(hào)處理函數(shù)去接著執(zhí)行。如果信號(hào)處理函數(shù)中
使用了不可重入函數(shù),那么信號(hào)處理函數(shù)可能會(huì)修改原來進(jìn)程中
不應(yīng)該被修改的數(shù)據(jù),這樣進(jìn)程從信號(hào)處理函數(shù)中返回接著執(zhí)行時(shí),
可能會(huì)出現(xiàn)不可預(yù)料的后果。不可再入函數(shù)在信號(hào)處理函數(shù)中被視為
不安全函數(shù)。滿足下列條件的函數(shù)多數(shù)是不可再入的:
(1)使用靜態(tài)的數(shù)據(jù)結(jié)構(gòu),如getlogin(),gmtime(),getgrgid(),
getgrnam(),getpwuid()以及getpwnam()等等;
(2)函數(shù)實(shí)現(xiàn)時(shí),調(diào)用了malloc()或者free()函數(shù);
(3)實(shí)現(xiàn)時(shí)使用了標(biāo)準(zhǔn)I/O函數(shù)的。The Open Group視下列函數(shù)為可再入的:
_exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、
cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown() 、
close()、creat()、dup()、dup2()、execle()、execve()、
fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、
geteuid()、getgid()、getgroups()、getpgrp()、getpid()、
getppid()、getuid()、kill()、link()、lseek()、mkdir()、
mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、
read()、rename()、rmdir()、setgid()、setpgid()、setsid()、
setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、
sigfillset()、sigismember()、signal()、sigpending()、
sigprocmask()、sigsuspend()、sleep()、stat()、sysconf()、
tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、
tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、
umask()、uname()、unlink()、utime()、wait()、waitpid()、
write()。
即使信號(hào)處理函數(shù)使用的都是"安全函數(shù)",同樣要注意進(jìn)入處理函數(shù)時(shí),
首先要保存errno的值,結(jié)束時(shí),再恢復(fù)原值。因?yàn)椋盘?hào)處理過程中,
errno值隨時(shí)可能被改變。另外,longjmp()以及siglongjmp()沒有被列為可重入函數(shù),
因?yàn)椴荒鼙WC緊接著兩個(gè)函數(shù)的其它調(diào)用是安全的。