如有错误,欢迎批评指正,本人也是才学APUE的菜鸟
/* sleep2 Code*/
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
static jmp_buf env_alrm;
static void sig_alrm(int signo) {
longjmp(env_alrm. 1);
}
unsigned int sleep2(unsigned int seconds) {
if(signal(SIGALRM, sig_alrm) == SIG_ERR)
return seconds;
if(setjmp(env_alrm) == 0) {
alarm(seconds);
pause();
}
return alarm(0);
}
- 由于直接调用 setjmp 的返回值会 0。所以调用 sleep2 时会直接进入 if 条件。
- 现假设 alarm(seconds) 与 pause() 函数,在繁忙的系统中,在程序未执行 pause 之前,定时器就超时了,捕捉到 SIGALRM信号,调用 sig_alrm(),然后 longjmp 会跳到 setjmp 的位置,此时 setjmp 的返回值为 1,就不会执行 if 条件中的语句。所以 sleep2 完全可以避免 sleep1 中的 alarm 先超时,然后调用 pause 使进程 (在未捕捉其他信号前) 永远挂起的情况。
书中说明了 sleep2 的缺陷:如果 SIGALRM 中断了某个其他处理程序,则调用 longjmp 会提早终止该信号的处理程序。
Code 10-9:sleep2 的缺陷,在一个捕捉信号的程序中调用 sleep2
#include "apue.h"
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
unsigned int sleep2(unsigned int);
static void sig_int(int);
int main(void) {
unsigned int unslept;
if(signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
unslept = sleep2(5);
printf("sleep2 returned: %u\n", unslept);
exit(0);
}
static void sig_int(int signo) {
int i,j;
volatile int k;
printf("\nsig_int starting\n");
for(i = 0;i < 300000;++ i)
for(j = 0;j < 4000;++ j)
k += i * j;
printf("sig_int finished\n");
}
在终端上,运行结果是这样的:
hjm@hjm-Inspiron:~/InterviewPreparation/apue/Examples/10-9$ ./10-9
^C
sig_int starting
sleep2 returned: 0
从输出中也可以看出,确实 sig_int 并未执行完成,而且 sleep2 也确实休眠了有 5s 的时间。
至于为什么会这样,这肯定是用栈帧来分析的。见下图: