Chinaunix

标题: APUE2中信号交互处理的问题 [打印本页]

作者: xiaozhu2007    时间: 2007-12-26 23:17
标题: APUE2中信号交互处理的问题
这是APUE2中程序清单10_6。
APUE2中说当SIGALRM信号中断了其它信号处理程序,则调用longjmp函数会提早终止该信号处理程序。

在本程序中,当程序开始运行后,按下ctrl+c产生SIGINT信号,main捕捉到该信号后调用sig_int处理函数(这个
函数运行的时间超过5s),而程序运行5s后,sleep2函数产生SIGALRM信号,被sig_alrm信号处理程序捕捉到后,该
程序调用longjmp,按照APUE2说的意思,这个SIGALRM信号中断了sig_int信号处理程序(为什么会中断该信号处理
程序呢?SIGALRM信号系统默认动作是终止程序,但是在sleep2中已经捕捉了该信号改变了它的默认处理动作了阿),而它
调用了longjmp函数会终止sig_int信号处理程序,这里我就不明白了,sig_int函数是SIGALRM终止的呢还是longjmp终止的呢?
这里SIGALRM信号被捕捉已经不会终止进程了,而longjmp函数好像也没有终止进程的功能吧?


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <setjmp.h>
  4. #include <signal.h>
  5. #include <unistd.h>

  6. static jmp_buf env_alrm;
  7. static void sig_alrm(int signo);
  8. static void sig_int(int signo);
  9. unsigned int sleep2(unsigned int nsecs);

  10. static void sig_alrm(int signo)
  11. {
  12.         longjmp(env_alrm, 1);
  13. }

  14. unsigned int sleep2(unsigned int nsecs)
  15. {
  16.         if(signal(SIGALRM, sig_alrm) == SIG_ERR){
  17.                 perror("signal error");
  18.                 exit(1);
  19.         }

  20.         if(setjmp(env_alrm) == 0){
  21.                 alarm(nsecs);
  22.                 pause();
  23.         }

  24.         return alarm(0);
  25. }

  26. static void sig_int(int signo)
  27. {
  28.         int                     i, j;
  29.         volatile int            k;

  30.         printf("caught SIGINT!\n");
  31. //      sleep(10);
  32.         for(i = 0; i < 10000000; i++){
  33.                 for(j = 0; j < 10000; j++){
  34.                         k += i * j;
  35.                 }
  36.         }
  37.         printf("after sig_int!\n");
  38. }

  39. int main(int argc, char** argv)
  40. {
  41.         unsigned int            unslept;

  42.         if(signal(SIGINT, sig_int) == SIG_ERR){
  43.                 perror("signal error");
  44.                 exit(2);
  45.         }

  46.         unslept = sleep2(5);
  47.         printf("sleep2 returned : %d\n", unslept);

  48.         exit(0);
  49. }
复制代码

[ 本帖最后由 xiaozhu2007 于 2007-12-27 00:22 编辑 ]
作者: lenovo    时间: 2007-12-26 23:17
我试着给你解释一下。
sig_int函数应该是longjmp终止的。
因为当调用longjmp时,它跳转到slee2函数中,
这样它把sig_int的栈帧给跳了过去,sig_int函数
就莫名的结束的。你做一下习题10.3,
如果对栈帧不太了解,参考setjmp和longjmp那一节。
作者: 主旋律    时间: 2007-12-26 23:55
汗,你主程序那里应该调用 sleep2 吧? 怎么调用的是 sleep? 这样一来 setjmp 没有运行,alrm 也没有设置。
作者: xiaozhu2007    时间: 2007-12-27 00:23
标题: 回复 #2 主旋律 的帖子
不好意思大意了,改过来了,不过问题还是不明白!
作者: zx_wing    时间: 2007-12-27 15:34
原帖由 xiaozhu2007 于 2007-12-26 23:17 发表
这是APUE2中程序清单10_6。
APUE2中说当SIGALRM信号中断了其它信号处理程序,则调用longjmp函数会提早终止该信号处理程序。

在本程序中,当程序开始运行后,按下ctrl+c产生SIGINT信号,main捕捉到该信号后调 ...

如lenovo所说,它是被longjmp中断的。
linux中,当一个信号在执行它的hanlder的时候,只会阻塞同类型的信号,例如这里在处理SIGINT,该信号就被阻塞至处理结束。但其它信号还是会打断当前信号的处理的。除非你自己将它屏蔽掉了。
所以这里在sig_int中执行的时候,SIGALRM信号到来,由于先前已设置了它的处理函数,会转入sig_alrm执行。sig_alrm执行了longjmp,跳转到setjmp之后继续执行,也就是SIGALRM信号处理完成,返回。程序继续运行直到结束。所以你应该可以看到 printf("sleep2 returned : %d\n", unslept);的输出。程序是正常执行结束的,被中断的只是SIGINT的处理
作者: xiaozhu2007    时间: 2007-12-27 22:30
明白了。
没有执行到longjmp的时候栈空间是这样的:
(栈的底部,高地址)
   main的栈帧
   sleep2的栈帧
   sig_int的栈帧
   sig_alrm的栈帧

当sig_alrm调用longjmp函数的时候,程序跳转到调用setjmp的地方即main栈帧继续执行,
此时栈空间是这样的:
(栈的底部,高地址)
   main的栈帧
   sleep2的栈帧

所以sig_int函数是被longjmp终止的。
我思考这个问题的时候咋就没想到longjmp会跳跃珍帧的问题呢?笨啊!
作者: xiaozhu2007    时间: 2007-12-27 22:55
如lenovo所说,它是被longjmp中断的。
linux中,当一个信号在执行它的hanlder的时候,只会阻塞同类型的信号,例如这里在处理SIGINT,该信号就被阻塞至处理结束。但其它信号还是会打断当前信号的处理的。除非你自己将它屏蔽掉了。

1。其中“但其它信号还是会打断当前信号的处理的。除非你自己将它屏蔽掉了。”是不是应该加上“或者设置了该信号的处理程序”
因为执行下面的程序,一开始按下ctrl+c,程序执行sig_int,当5s后SIGALRM信号被处理程序sig_alrm捕捉的时候,并不打断当前sig_int处理程序。
2。还有,linux的sleep函数是不是使用alarm函数实现的,所以,在同一个程序中,最好不要同时使用sleep和alarm函数,否则会出现alarm函数的设置覆盖,
比如本例子sig_int中的程序段:
        for(i = 0; i < 10000000; i++){
                for(j = 0; j < 10000; j++){
                        k += i * j;
                }
        }
就不可以使用sleep函数代替,因为main中有alarm,否则程序不会出现正确的结果。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>

  4. void sig_int(int signo);
  5. void sig_alrm(int signo);

  6. int main(int argc, char** argv)
  7. {
  8.         if(signal(SIGINT, sig_int) == SIG_ERR){
  9.                 perror("signal error");
  10.                 exit(1);
  11.         }
  12.         if(signal(SIGALRM, sig_alrm) == SIG_ERR){
  13.                 perror("signal error");
  14.                 exit(2);
  15.         }
  16.         alarm(5);
  17.         pause();

  18.         exit(0);
  19. }

  20. void sig_int(int signo)
  21. {
  22.         int                     i, j;
  23.         volatile int            k;

  24.         printf("begin sig_int\n");

  25.         for(i = 0; i < 10000000; i++){
  26.                 for(j = 0; j < 10000; j++){
  27.                         k += i * j;
  28.                 }
  29.         }

  30.         printf("end sig_int\n");
  31. }

  32. void sig_alrm(int signo)
  33. {
  34.         printf("caught SIGALRM\n");
  35. }
复制代码

作者: zx_wing    时间: 2007-12-28 13:06
标题: 回复 #7 xiaozhu2007 的帖子
>>1。其中“但其它信号还是会打断当前信号的处理的。除非你自己将它屏蔽掉了。”是不是应该加>>上“或者设置了该信号的处理程序”
>>因为执行下面的程序,一开始按下ctrl+c,程序执行sig_int,当5s后SIGALRM信号被处理程序s>>ig_alrm捕捉的时候,并不打断当前sig_int处理程序。
不,sig_int会被打断。新的信号(非同一个信号)会打断当前信号处理程序的执行。所以你应该看不到printf("after sig_int!\n");这句的输出
作者: baohuaihuai    时间: 2007-12-28 13:45
原帖由 xiaozhu2007 于 2007-12-27 22:55 发表

1。其中“但其它信号还是会打断当前信号的处理的。除非你自己将它屏蔽掉了。”是不是应该加上“或者设置了该信号的处理程序”
因为执行下面的程序,一开始按下ctrl+c,程序执行sig_int,当5s后SIGALRM信号被 ...


当然打断啊,只是执行完ALARM的信号处理函数后就返回刚才的上下文了.
作者: xiaozhu2007    时间: 2007-12-28 15:41
不对吧,我说的是下面的程序啊!当产生了SIGALRM信号的时候,并没有中断sig_int程序哦!!!

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>

  4. void sig_int(int signo);
  5. void sig_alrm(int signo);

  6. int main(int argc, char** argv)
  7. {
  8.         if(signal(SIGINT, sig_int) == SIG_ERR){
  9.                 perror("signal error");
  10.                 exit(1);
  11.         }
  12.         if(signal(SIGALRM, sig_alrm) == SIG_ERR){
  13.                 perror("signal error");
  14.                 exit(2);
  15.         }
  16.         alarm(5);
  17.         pause();

  18.         exit(0);
  19. }

  20. void sig_int(int signo)
  21. {
  22.         int                     i, j;
  23.         volatile int            k;

  24.         printf("begin sig_int\n");

  25.         for(i = 0; i < 10000000; i++){
  26.                 for(j = 0; j < 10000; j++){
  27.                         k += i * j;
  28.                 }
  29.         }

  30.         printf("end sig_int\n");
  31. }

  32. void sig_alrm(int signo)
  33. {
  34.         printf("caught SIGALRM\n");

复制代码

作者: zx_wing    时间: 2007-12-28 16:41
原帖由 xiaozhu2007 于 2007-12-28 15:41 发表
不对吧,我说的是下面的程序啊!当产生了SIGALRM信号的时候,并没有中断sig_int程序哦!!!

#include
#include
#include

void sig_int(int signo);
void sig_alrm(int signo);

int main(int ar ...

打断的意思是,sig_int还没执行完,就执行sig_alrm去了。所以你应该看到打印出了begin sig_int后接着看到的是caught SIGALRM,最后是end sig_int。打断的意思是暂时中断当前执行,稍后再恢复执行。也就是这里执行的顺序是sig_int --->被打断--->sig_alrm--->接着执行未完成的sig_int。
作者: xiaozhu2007    时间: 2007-12-28 16:59
标题: 回复 #11 zx_wing 的帖子
哦我以为是中断呢,原来是暂时打断啊!学习了。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2