如有错误,欢迎批评指正,本人也是才学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("in 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$ in sig_alrm:
finishing sig_usr1:
ending main:
可以看到,这里的输出和书上明显不同。即,没有像书上说的那样,当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动地加到进程的信号屏蔽字中。
PS:实际原因,见博客最后的更新部分。。。。。!!!中间的是 博猪 踩得坑
接着我将 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$ in 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$ in 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$ in sig_alrm: SIGUSR1
finishing sig_usr1: SIGUSR1
ending main: SIGUSR1
所以,人有点懵。。
------------同一天下午,更新-------------
今天下午,也是书上的程序,怎么搞都和书上不一样,然后搜到了一篇博客,复制了它的相同代码。最后,这复制的代码是正确的,它能够正常输出。。。
我用尽了一切办法,最后终于发现了代码总是输出不正确的原因,#include “apue.h” 放在头文件的最后,就可以正确了。
这让我想起了上午的那个程序 (没错,就是实例 10-20),在百度博客的时候看见了一个人的评论:把头文件顺序改一下就可以了。(当然,我试过,但是是瞎jb改的顺序,没成功)
这一次,我改正确了!!!输出和书上终于一毛一样了!!!意味着,上午的代码,,,呸,忘记吧!!。。。。
我应该高兴还是失落?????????
最后,终端运行如下,证实了当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动地加到进程的信号屏蔽字中。
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ gcc -o 10-20 10-20.c -lapue
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 19729
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 19729
starting sig_usr1: SIGUSR1
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ in sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
ending main:
[1]+ 已完成 ./10-20
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ ./10-20 &
[1] 22208
starting main:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 22208
starting sig_usr1: SIGUSR1
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 22208
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 22208
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ kill -USR1 22208
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-20$ int sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
ending main: