如有錯誤,歡迎批評指正,本人也是才學APUE的菜鳥
實驗的系統是 Ubuntu 18.04
書中在這一節一開始就提到了一句話:當捕捉到一個信號時,進入信號捕捉函數,此時當前信號被自動地加到進程的信號屏蔽字中。
接着按照書上的代碼:
#include "apue.h"
#include <errno.h>
#include <time.h>
#include <setjmp.h>
void pr_mask(const char*);
static void sig_usr1(int);
static void sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;
int main(void) {
if(signal(SIGUSR1, sig_usr1) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if(signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
pr_mask("starting main: ");
if(sigsetjmp(jmpbuf, 1)) {
pr_mask("ending main: ");
exit(0);
}
canjump = 1;
for( ; ; )
pause();
}
static void sig_usr1(int signo) {
time_t starttime;
if(canjump == 0) return ;
pr_mask("starting sig_usr1: ");
alarm(3);
starttime = time(NULL);
for( ; ; )
if(time(NULL) > starttime + 5)
break;
pr_mask("finishing sig_usr1: ");
canjump = 0;
siglongjmp(jmpbuf, 1);
}
static void sig_alrm(int signo) {
pr_mask("int sig_alrm: ");
}
void pr_mask(const char *str) {
sigset_t sigset;
int errno_save;
errno_save = errno; // we can be called by signal handlers
if(sigprocmask(0, NULL, &sigset) < 0)
err_ret("sigprocmask error");
else {
printf("%s", str);
if(sigismember(&sigset, SIGINT))
printf(" SIGINT");
if(sigismember(&sigset, SIGQUIT))
printf(" SIGQUIT");
if(sigismember(&sigset, SIGUSR1))
printf(" SIGUSR1");
if(sigismember(&sigset, SIGALRM))
printf(" SIGALRM");
printf("\n");
}
errno = errno_save; // restore errno, because the sigxxx function maybe change errno
}
在終端上,得到了以下的運行結果:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 32353
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 32353
starting sig_usr1:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ int sig_alrm:
finishing sig_usr1:
ending main:
可以看到,這裏的輸出和書上明顯不同。即,沒有像書上說的那樣,當捕捉到一個信號時,進入信號捕捉函數,此時當前信號被自動地加到進程的信號屏蔽字中。
接着我將 sig_usr1 函數做了一點變動,在 pr_mask(“starting sig_usr1”) 後加入了 sleep,並嘗試多次發送 SIGUSR1 信號
static void sig_usr1(int signo) {
time_t starttime;
if(canjump == 0) return ;
pr_mask("starting sig_usr1: ");
alarm(3);
sleep(20);
starttime = time(NULL);
for( ; ; )
if(time(NULL) > starttime + 5)
break;
pr_mask("finishing sig_usr1: ");
canjump = 0;
siglongjmp(jmpbuf, 1);
}
在終端的運行情況:
# 這是在 SIGALRM 信號產生之後
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 3258
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3258
starting sig_usr1:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ int sig_alrm:
kill -USR1 3258
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3258
bash: kill: (3258) - 沒有那個進程
[1]+ 用戶定義信號 1 ./10-20
# 這是在 SIGALRM 信號產生之前
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 3265
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3265
starting sig_usr1:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3265
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3265
bash: kill: (3265) - 沒有那個進程
[1]+ 用戶定義信號 1 ./10-20
可以看到,發送了兩次 SIGUSR1 之後,該進程已經結束了 (第三次發送報錯)。
程序終止的原因:P259,早期的 signal 在進程每次接收到信號對其進行處理時,隨即將該信號動作重置爲默認值。而對 SIGUSR1,系統的默認處理方式是終止。
如果 SIGUSR1 在信號屏蔽字中,按理說應該是可以發送多次 SIGUSR1 的,並不會終止程序。
於是,我又將 sig_usr1 做了如下更改,手動將 SIGUSR1 加入屏蔽字,進行對比:
static void sig_usr1(int signo) {
time_t starttime;
if(canjump == 0) return ;
sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); sigprocmask(SIG_BLOCK, &mask, NULL);
pr_mask("starting sig_usr1: ");
alarm(3);
sleep(20);
starttime = time(NULL);
for( ; ; )
if(time(NULL) > starttime + 5)
break;
pr_mask("finishing sig_usr1: ");
canjump = 0;
siglongjmp(jmpbuf, 1);
}
終端運行的情況:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 3782
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
starting sig_usr1: SIGUSR1
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 3782
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ int sig_alrm: SIGUSR1
finishing sig_usr1: SIGUSR1
[1]+ 用戶定義信號 1 ./10-20
可以看見,確實可以發送多個 SIGUSR1,而進程也沒有被直接終止。
??不過疑問的是:爲什麼最後的輸出沒有 ending main …
但如果只鍵入一次 kill -USR1,是有 ending main 的。同時,在 ending main 中,信號屏蔽字中沒有 SIGUSR1:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 4501
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 4501
starting sig_usr1: SIGUSR1
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ int sig_alrm: SIGUSR1
finishing sig_usr1: SIGUSR1
ending main:
如果這種情況下,將 sigsetjmp 與 siglongjmp 換成 setjmp 與 longjmp,即使多次鍵入 kill -USR1,仍會輸出 ending main,同時信號屏蔽字中包含 SIGUSR1
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 5031
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
starting sig_usr1: SIGUSR1
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 5031
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ int sig_alrm: SIGUSR1
finishing sig_usr1: SIGUSR1
ending main: SIGUSR1
所以,人有點懵。。