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
- #include<sys/types.h>
- #include<signal.h>
- int kill(pid_t pid, int sig) ;
- int raise(int sig) ;
- int alarm(int seconds);
- int setitimer(int which ,struct itimerval *new_file, struct itimerval *old_file) ;
-
-
-
2,信号的屏蔽
有些时候我们希望我们的进程正常地执行,而不希望进程受到其他信号的影响,这时候我们就可以就一些信号变成进程的屏蔽信号,从而实现进程对某些信号的屏蔽。常用的函数有:
- #include<signal.h>
- int sigemptyset(sigset_t *set) ;
- int sigfillset(sigset_t *set);
- int sigaddset(sigset_t *set , int signo);
- int sigdelset(sigset_t *set , int signo);
- int sigismember(sigset_t *set , int signo)
- int sigprocmask(int how , sigset_t *set , sigset_t *oset )
- how = SIG_BLOCK : 增加一个信号集合到当前的阻塞集合中去
- SIG_UNBLOCK:从当前的阻塞集合中删除一个信号的集合
- SIG_SETMASK:将当前的信号集合设置为信号阻塞集合
具体的实现是:首先将set清空,将需要屏蔽的信号添加到set中去,然后利用系统调用sigprocmask将set集合中的信号添加到进程的阻塞集合中去,这时候我们的进程就对这些信号又“屏蔽”作用了,即:进程对这些信号不会有响应。但是这种不响应只是暂时的,进程不是将信号忽略,而是先将其存放在输入缓存中,待进程对该信号不阻塞之后,进程还是会去执行刚才的信号。
下面是一个例程:
-
-
-
- #include<stdio.h>
- #include<stdlib.h>
- #include<signal.h>
- #include<unistd.h>
- int main(int argc ,char **argv){
- sigset_t intmask ;
- int i ;
- sigemptyset(&intmask) ;
- sigaddset(&intmask , SIGINT);
- while(1){
- sigprocmask(SIG_BLOCK,&intmask ,NULL) ;
- fprintf(stderr, "SIGINT signal blocked!\n");
- for(i = 0;i<5;++i){
- fprintf(stderr, "%d ",i);
- sleep(1) ;
- }
- fprintf(stderr,"\n\n\n" );
- fprintf(stderr , "Blocked calculation is finished!\n");
- sigprocmask(SIG_UNBLOCK,&intmask , NULL);
- fprintf(stderr, "SIGINT signal unblocked!\n");
- for(i=0;i<8;++i){
- fprintf(stderr, "%d ",i);
- sleep(1) ;
- }
- fprintf(stderr , "\n");
- }
- return 0 ;
- }
程序运行的时候,在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存放进程之前对该信号的处理动作。
- struct sigaction{
- void (*sa_handler)(int signo) ;
- void (*sa_sigaction)(int signo , siginfo_t *info, void *act) ;
- sigset_t sa_mask ;
- int sa_flags ;
- void (*sa_restore)(void) ;
- }
- sa_handler : 函数指针,指向进行信号操作的函数。
- sa_flags : 设置信号操作的情况, 一般设置为0
- sa_mask : 存放要屏蔽的信号。
下面是一个实例:
-
-
-
- #include<stdio.h>
- #include<unistd.h>
- #include<signal.h>
- #include<sys/types.h>
- #include<string.h>
- #include<errno.h>
- #define PROMT "你想终止程序吗?"
- char *promt = PROMT ;
- void ctrl_c(int sig){
- write(STDERR_FILENO,promt,strlen(promt)) ;
- char choose[5] ;
- fscanf(stdin,"%s",choose);
- if(choose[0] == 'Y' || choose[0] == 'y'){
- _exit(0) ;
- }
- }
- int main(){
- struct sigaction act ;
- act.sa_handler = ctrl_c ;
- sigemptyset(&act.sa_mask) ;
- act.sa_flags = 0 ;
- if(sigaction(SIGINT , &act , NULL) <0){
- fprintf(stderr, "Install Signal Action Error:%s!\n\n",strerror(errno));
- _exit(1) ;
- }
- while(1) ;
- return 0;
- }
进程接受用户发出的SIGINT信号然后定义自己的动作act,即输出一句话,确认用户是否真的想退出。并接受选择。
4,pause 和 sigsuspend 函数
pause()函数只是简单地挂起进程,直至一个终止信号,或是应用信号传到,进程才被唤醒。
sigsuspend(sigset_t *set)函数,暂时性地将set代替进程的信号屏蔽mask,在set中的信号都将被视为屏蔽信号,即不作任何反映,直至一个不再set中的信号的到来才能将进程唤醒。同样地,之前输入的信号也并没有丢弃,只是在进程被唤醒之后再执行。
- <span style="font-size:16px;">
-
-
- #include<stdio.h>
- #include<signal.h>
- #include<unistd.h>
-
-
- void ope(int ) ;
-
- int flag ;
- int main(){
- sigset_t new_mask , old_mask , zero_mask ;
- flag = 0 ;
- struct sigaction act ;
-
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0 ;
- act.sa_handler = ope ;
-
- sigaction(SIGINT , &act , NULL );
- sigaction(SIGQUIT , &act , NULL );
- printf("ls -s %d %d\n",SIGQUIT,getpid());
- sigemptyset(&new_mask);
- sigaddset(&new_mask , SIGQUIT) ;
- sigprocmask(SIG_BLOCK,&new_mask,&old_mask) ;
- sigemptyset(&zero_mask);
- while(flag == 0){
-
-
- sigsuspend(&zero_mask) ;
- }
- sigprocmask(SIG_SETMASK,&old_mask ,NULL);
- return 0 ;
- }
-
- void ope(int sig){
- switch(sig){
- case SIGINT :
- printf("SIGINT have caught!\n");
- break ;
- case SIGQUIT:
- printf("SIGQUIT have caught!\n");
- flag = 1 ;
- break ;
- }
- }</span>
sigaction 精讲:
- <span style="font-size:16px;">#include<signal.h>
- int sigaction(int sig, struct sigaction *act , struct sigaction *oact) ;
-
- struct sigaction{
- void (*sa_handler)(int);
- void (*sa_sigaction)(int, siginfo_t *, void *);
- sigset_t sa_mask;
- int sa_flags;
- void (*sa_restorer)(void);
- }
- </span>
这个函数可以
:
1. 给一个signal安装一个handler,并且在使用sigaction修改该handler之前,不用reinstall
2. 使用sigaction结构,该结构包含handler,其中可以指定2个handler,一个是使用sigiinfo_t等参数的handler,即支持给handler更多的参数,使其可以知道自己是被什么进程,那个用户,发来的什么信号,发来该信号的具体的原因是什么,当然要像这样,得给sigaction的sa_flags设置SA_SIGINFO标记。
3.使用sigaction的sa_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加入sigaction的sa_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
也就退出了。
- <span style="font-size:16px;">#include <unistd.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/wait.h>
-
- void nullhandler( int num )
- {
- puts( "child received signal" );
- puts( "child sleep 5 secs in handler..." );
- sleep(5);
- puts( "child wake up in handler after 5 secs" );
- }
-
- int main()
- {
- setbuf( stdout, NULL );
- int pid = fork();
- if( pid == 0 )
- {
-
- puts("child started");
- printf("%d",getpid());
-
-
-
-
-
-
- struct sigaction act, oldact;
- act.sa_handler = nullhandler;
- sigemptyset( &act.sa_mask );
- if( sigaction( SIGUSR1, &act,0 ) < 0 )
- {
- puts(" child install handler failed");
- return -1;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- puts("child waiting for signal...");
-
- pause();
- puts("child returnd from signal handler");
- puts("child is quiting");
- exit(0);
- }
- sleep(1);
- puts( " father send SIGUSR1 once" );
- int ret = kill( pid, SIGUSR1 );
- puts("father sleep 1 sec to ensure child is now in signal handler");
- sleep(1);
- puts( " father send SIGUSR1 twice" );
- ret = kill( pid, SIGUSR1 );
- puts( " father send SIGUSR1 third times" );
- ret = kill( pid, SIGUSR1 );
-
- waitpid( pid, 0, 0);
- puts("father is quiting");
- return 0;
- }</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的作用域内,一次会将所有相同信号当一次执行。