自学linux信号

1,信号的产生:

Linux下可以用kill -l命令查看所有的信号:

1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP  6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1    11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM     16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP    21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ    26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR   31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36)SIGRTMIN+2    37) SIGRTMIN+3      38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8  43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13     48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12    53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7   58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2    63) SIGRTMAX-1    64) SIGRTMAX    

其中最常用的命令是:kill , raise , alarm , setitimer

[cpp]  view plain copy
  1. #include<sys/types.h>  
  2. #include<signal.h>  
  3. int kill(pid_t pid, int sig) ; /*将信号sig发送给pid指定的进程(pid>0)*/  
  4. int raise(int sig) ;   /*进程向自己发送一个sig信号*/  
  5. int alarm(int seconds);   /*进程经过指定的时间之后向<span style="background-color: rgb(255, 0, 0);">自己</span>发送一个SIGALRM信号*/  
  6. int setitimer(int which ,struct itimerval *new_file, struct itimerval *old_file) ;  
  7. /* 
  8. struct itimerval 结构体存放的是系统的时间, which 是mode 
  9. */  


2,信号的屏蔽

      有些时候我们希望我们的进程正常地执行,而不希望进程受到其他信号的影响,这时候我们就可以就一些信号变成进程的屏蔽信号,从而实现进程对某些信号的屏蔽。常用的函数有:

[cpp]  view plain copy
  1. #include<signal.h>  
  2. int sigemptyset(sigset_t *set) ;    //初始化set集合,使set集合为空  
  3. int sigfillset(sigset_t *set);      //初始化set集合,使set集合包含所有的信号  
  4. int sigaddset(sigset_t *set , int signo);   //将signo信号加入到set集合中  
  5. int sigdelset(sigset_t *set , int signo);   //将signo信号从set集合中删除  
  6. int sigismember(sigset_t *set , int signo)  //查询信号是否在集合set中  
  7. int sigprocmask(int how , sigset_t *set , sigset_t *oset ) //将set集合中的信号加入到进程的阻塞集合中去  
  8. how = SIG_BLOCK : 增加一个信号集合到当前的阻塞集合中去  
  9.       SIG_UNBLOCK:从当前的阻塞集合中删除一个信号的集合  
  10.       SIG_SETMASK:将当前的信号集合设置为信号阻塞集合  
具体的实现是:首先将set清空,将需要屏蔽的信号添加到set中去,然后利用系统调用sigprocmask将set集合中的信号添加到进程的阻塞集合中去,这时候我们的进程就对这些信号又“屏蔽”作用了,即:进程对这些信号不会有响应。但是这种不响应只是暂时的,进程不是将信号忽略,而是先将其存放在输入缓存中,待进程对该信号不阻塞之后,进程还是会去执行刚才的信号。

下面是一个例程:

[cpp]  view plain copy
  1. /* 
  2. 信号的屏蔽 
  3. */  
  4. #include<stdio.h>  
  5. #include<stdlib.h>  
  6. #include<signal.h>  
  7. #include<unistd.h>  
  8. int main(int argc ,char **argv){  
  9.     sigset_t intmask ;  
  10.     int i ;  
  11.     sigemptyset(&intmask) ;  
  12.     sigaddset(&intmask , SIGINT);  
  13.     while(1){  
  14.         sigprocmask(SIG_BLOCK,&intmask ,NULL) ;  
  15.         fprintf(stderr, "SIGINT signal blocked!\n");  
  16.         for(i = 0;i<5;++i){  
  17.             fprintf(stderr, "%d ",i);  
  18.             sleep(1) ;  
  19.         }  
  20.         fprintf(stderr,"\n\n\n" );  
  21.         fprintf(stderr , "Blocked calculation is finished!\n");  
  22.         sigprocmask(SIG_UNBLOCK,&intmask , NULL);   
  23.         fprintf(stderr, "SIGINT signal unblocked!\n");  
  24.         for(i=0;i<8;++i){  
  25.             fprintf(stderr, "%d ",i);  
  26.             sleep(1) ;        
  27.         }  
  28.         fprintf(stderr , "\n");  
  29.     }  
  30.     return 0 ;  
  31. }  
     程序运行的时候,在while中的第一个for循环,因为这时候进程对信号SIGINT是屏蔽的,因此在第一个for执行阶段,当输入CTRL +C试图终止进程的时候,是失败的,因为这时候进程对SIGINT信号(CTRL + C)是阻塞的,但是等到第一个for循环结束之后,程序会马上结束,因为进程这时候恢复了对SIGINT信号的作用,结束进程。


3,接受一个信号,并作相应的处理:

     有时候我们想改变进程对一个信号的动作,即当进程接受这个信号之后,是运行自己定义的一个函数,而不是运行默认的命令。这时候我们可以用signal(int sig, function)来实现这个功能, 也可以用sigaction(int sig , struct sigaction *act ,struct sigaction *oact)函数来实现这个功能。 其中的sig是待处理的信号,act是一个struct sigaction结构体,用来定义用户对该信号的响应 , oact存放进程之前对该信号的处理动作。

[cpp]  view plain copy
  1. struct sigaction{  
  2.     void (*sa_handler)(int signo) ;  
  3.     void (*sa_sigaction)(int signo , siginfo_t *info, void *act) ;  
  4.     sigset_t sa_mask ;  
  5.     int sa_flags ;  
  6.     void (*sa_restore)(void) ;  
  7. }  
  8. sa_handler : 函数指针,指向进行信号操作的函数。  
  9. sa_flags : 设置信号操作的情况, 一般设置为0  
  10. sa_mask : 存放要屏蔽的信号。  

下面是一个实例:

[cpp]  view plain copy
  1. /* 
  2. 接受一个规定的信号,并执行相应的命令。 
  3. */  
  4. #include<stdio.h>  
  5. #include<unistd.h>  
  6. #include<signal.h>  
  7. #include<sys/types.h>  
  8. #include<string.h>  
  9. #include<errno.h>  
  10. #define PROMT "你想终止程序吗?"  
  11. char *promt = PROMT ;  
  12. void ctrl_c(int sig){  
  13.     write(STDERR_FILENO,promt,strlen(promt)) ;  
  14.     char choose[5] ;  
  15.     fscanf(stdin,"%s",choose);  
  16.     if(choose[0] == 'Y' || choose[0] == 'y'){  
  17.         _exit(0) ;    
  18.     }  
  19. }  
  20. int main(){  
  21.     struct sigaction act ;  
  22.     act.sa_handler = ctrl_c ;  
  23.     sigemptyset(&act.sa_mask) ;  
  24.     act.sa_flags = 0 ;  
  25.     if(sigaction(SIGINT , &act , NULL) <0){  
  26.         fprintf(stderr, "Install Signal Action Error:%s!\n\n",strerror(errno));  
  27.         _exit(1) ;  
  28.     }  
  29.     while(1) ;  
  30.     return 0;  
  31. }  
进程接受用户发出的SIGINT信号然后定义自己的动作act,即输出一句话,确认用户是否真的想退出。并接受选择。


4,pause 和 sigsuspend 函数

      pause()函数只是简单地挂起进程,直至一个终止信号,或是应用信号传到,进程才被唤醒。

     sigsuspend(sigset_t *set)函数,暂时性地将set代替进程的信号屏蔽mask,在set中的信号都将被视为屏蔽信号,即不作任何反映,直至一个不再set中的信号的到来才能将进程唤醒。同样地,之前输入的信号也并没有丢弃,只是在进程被唤醒之后再执行。

[cpp]  view plain copy
  1. <span style="font-size:16px;">/* 
  2. Linux信号 sigsuspend和sigaction结合使用。 
  3. */  
  4. #include<stdio.h>  
  5. #include<signal.h>  
  6. #include<unistd.h>  
  7.   
  8. /*处理信号的函数*/  
  9. void ope(int ) ;  
  10. /*标记变量*/  
  11. int flag ;   
  12. int main(){   
  13.     sigset_t new_mask , old_mask , zero_mask ;   
  14.     flag = 0 ;  
  15.     struct sigaction act ;  
  16.     /*定义和初始化act结构*/   
  17.     sigemptyset(&act.sa_mask);  
  18.     act.sa_flags = 0 ;  
  19.     act.sa_handler = ope ;  
  20.     /*注册信号SIGINT和SIGQUIT信号的处理函数*/   
  21.     sigaction(SIGINT , &act , NULL );  
  22.     sigaction(SIGQUIT , &act , NULL );  
  23.     printf("ls -s %d %d\n",SIGQUIT,getpid());  
  24.     sigemptyset(&new_mask);     /*清空new_mask*/  
  25.     sigaddset(&new_mask , SIGQUIT) ;    /*将SIGQUIT信号加入到 new_mask中*/  
  26.     sigprocmask(SIG_BLOCK,&new_mask,&old_mask) ;  
  27.     sigemptyset(&zero_mask);  
  28.     while(flag == 0){  
  29.         /*用一个空的set当作进程的信号屏蔽,即进程接受任何信号都将被唤醒,但是由于flag的值未改变,因此还在while中 
  30.         直到一个不SIGQUIT的出现将flag变为1,进程才结束。*/  
  31.         sigsuspend(&zero_mask) ;      
  32.     }     
  33.     sigprocmask(SIG_SETMASK,&old_mask ,NULL);     
  34.     return 0 ;  
  35. }  
  36. /*信号处理函数*/  
  37. void ope(int sig){  
  38.     switch(sig){  
  39.         case SIGINT :  
  40.             printf("SIGINT have caught!\n");  
  41.             break ;  
  42.         case SIGQUIT:     
  43.             printf("SIGQUIT have caught!\n");  
  44.             flag = 1 ;  
  45.             break ;  
  46.     }  
  47. }</span>  


sigaction 精讲:
[cpp]  view plain copy
  1. <span style="font-size:16px;">#include<signal.h>  
  2. int sigaction(int sig, struct sigaction *act , struct sigaction *oact) ;  
  3.   
  4. struct sigaction{  
  5.       void     (*sa_handler)(int);  
  6.       void     (*sa_sigaction)(int, siginfo_t *, void *);  
  7.       sigset_t   sa_mask;  
  8.       int        sa_flags;  
  9.       void     (*sa_restorer)(void);  
  10. }  
  11. </span>  
这个函数可以 :

1. 给一个signal安装一个handler,并且在使用sigaction修改该handler之前,不用reinstall

2. 使用sigaction结构,该结构包含handler,其中可以指定2handler,一个是使用sigiinfo_t等参数的handler,即支持给handler更多的参数,使其可以知道自己是被什么进程,那个用户,发来的什么信号,发来该信号的具体的原因是什么,当然要像这样,得给sigactionsa_flags设置SA_SIGINFO标记。

3.使用sigactionsa_flags标记还可以指定系统调用被这个信号打断后,是直接返回,还是自动restart. 一个典型就是,一般我们不让SIGALRM信号将被打断的系统调用restart,因为SIGALARM一般本来就是用来打断一个block的调用的。

4. 为了模仿老的signal函数的作用,实现unreliable 的类似signal的操作,可以通过给sa_flags设置SA_RESETHAND使handler不会自动reinstall,以及SA_NODEFER标记来使在本信号的handler内部,本信号不被自动block,当然如果你手动在sa_mask中指定要block本信号的话就可以将其block了。

5. 通过使用sigaction结构中的sa_mask,可以在该handler执行的过程中,block一些信号,注意,这个mask是与我们使用sigprocmask设置的mask不同的mask,这个mask的作用范围仅限于本handler函数,而且他不会将我们用sigprocmask设置的mask取消,而仅仅是在其基础上再次将一些信号block掉,当handler结束时,系统会自动将mask恢复成以前的样子,所以这个sigaction中的sa_mask只作用本信号的handler的执行时间。

此外,系统为了避免一个signal handler的执行的时候再次被本signal打断,就自动在本handler执行之前,将本signal加入sigactionsa_mask中,使本handler的执行过程中,不会受到本signal的嵌套打扰,单是如果本handler对应的信号的确发生了,那么该信号会在本handler执行完后执行,但只执行一次,因为只能记录一次,当然如果在这次新的执行中,又发生了这种情况,应该往复下去。下面就是一段代码,它验证了如下几点:

1).Sigaction会使handler自动将本signal给临时block

2).在一个handler执行过程中被临时block掉的信号也会被记录,等handler完成后会被delivery

下例子中, child 一开始就 pause() 等待信号来临, father 给他发送 SIGUSR1 信号,然后 father 就进入 1 秒钟的睡眠,这是为了等 child 在他的 handler 里面进入睡眠。 Child 受到 SIGUSR1 后。立即执行 handler ,它会进入 5 秒钟的睡眠。那么可见,等 father 睡了 1 秒钟后, child 还在睡眠,并且在其 handler 里面睡眠。此时 father 可以连续发送 2 SIGUSR1 child ,我们发现 child 并不响应,而是依然睡足她的剩下的时间。 5 秒钟睡眠结束后, child 醒了,它的 handler 退出,系统自动将临时 block SIGUSR1 unblock ,此时发现有 pending()  SIGUSR1 ,因此将他 delivery child 。于是 child 再次进入 handler ,此时 father 已经不再发送信号了,就等着孩子结束呢。所以 handler 结束后, child 就继续执行,退出,然后 father 也就退出了。

[cpp]  view plain copy
  1. <span style="font-size:16px;">#include <unistd.h>  
  2. #include <signal.h>  
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <sys/wait.h>  
  6.   
  7. void nullhandler( int num )  
  8. {  
  9.        puts( "child received  signal" );  
  10.        puts( "child sleep 5 secs in handler..." );  
  11.        sleep(5);  
  12.        puts( "child wake up in handler after 5 secs" );  
  13. }  
  14.   
  15. int main()  
  16. {  
  17.        setbuf( stdout, NULL );  
  18.        int pid = fork();  
  19.        if( pid == 0 )  
  20.        {  
  21.               //child  
  22.               puts("child started");  
  23.           printf("%d",getpid());  
  24. /*           
  25.               sigset_t maskset,oldset,oldset1; 
  26.               sigemptyset( &maskset ); 
  27.               sigaddset( &maskset, SIGUSR1 ); 
  28.               sigprocmask( SIG_BLOCK, &maskset, &oldset ); 
  29. */  
  30.               struct sigaction act, oldact;  
  31.               act.sa_handler = nullhandler;  
  32.               sigemptyset( &act.sa_mask );  
  33.               if( sigaction( SIGUSR1, &act,0 ) < 0 )  
  34.               {  
  35.                      puts(" child install handler failed");  
  36.                      return -1;  
  37.               }  
  38.               /* 
  39.               puts("child went to sleep ..."); 
  40.               sleep(5); 
  41.               puts("child wake up..."); 
  42.               sigset_t pendset; 
  43.               if( sigpending( &pendset ) < 0  ) 
  44.               { 
  45.                      puts("get pending signal failed"); 
  46.                      return -1; 
  47.               } 
  48.               if( sigismember( &pendset, SIGUSR1 ) ) 
  49.                      puts("SIGUSR1 is pending signal"); 
  50.               else 
  51.                      puts("SIGUSR1 is pending signal"); 
  52.               puts("child is unblocking signal"); 
  53.               if( sigprocmask(SIG_UNBLOCK, &maskset, &oldset1 ) < 0 ) 
  54.                      puts("unblock signal failed"); 
  55.               else 
  56.                      puts("unblock signal success"); 
  57.               */  
  58.               puts("child waiting for signal...");  
  59.               /*pause函数只是简单地将进程挂起,直至进程接受到一个terminal信号,或是一个信号函数将信号捕捉,并进行调用。*/  
  60.               pause();  
  61.               puts("child returnd from signal handler");  
  62.               puts("child  is quiting");  
  63.               exit(0);  
  64.        }  
  65.        sleep(1);  
  66.        puts( " father send  SIGUSR1 once" );  
  67.        int ret = kill( pid, SIGUSR1 );  
  68.        puts("father sleep 1 sec to ensure child is now in signal handler");  
  69.        sleep(1);  
  70.        puts( " father send  SIGUSR1 twice" );  
  71.        ret = kill( pid, SIGUSR1 );  
  72.        puts( " father send  SIGUSR1 third times" );  
  73.        ret = kill( pid, SIGUSR1 );  
  74.        /*等待子进程的结束。*/  
  75.        waitpid( pid, 0, 0);  
  76.        puts("father is quiting");  
  77.        return 0;  
  78. }</span>  

输出结果:

child started

child waiting for signal...

father send  SIGUSR1 once

father sleep 1 sec to ensure child is now in signal handler

child received  signal

child sleep 5 secs in handler...

father send  SIGUSR1 twice

father send  SIGUSR1 third times

child wake up in handler after 5 secs

child received  signal

child sleep 5 secs in handler...

child wake up in handler after 5 secs

child returnd from signal handler

child  is quiting

         father is quiting


虽然pending的信号有两个,在signal1的作用域内,一次会将所有相同信号当一次执行。

你可能感兴趣的:(自学linux信号)