APUE 第三版 程序 10-20 (sigsetjmp 與 siglongjmp,及程序的問題??)

如有錯誤,歡迎批評指正,本人也是才學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

 
所以,人有點懵。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章