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) ; /*将信号sig发送给pid指定的进程(pid>0)*/
int raise(int sig) ; /*进程向自己发送一个sig信号*/
int alarm(int seconds); /*进程经过指定的时间之后向自己发送一个SIGALRM信号*/
int setitimer(int which ,struct itimerval *new_file, struct itimerval *old_file) ;
/*
struct itimerval 结构体存放的是系统的时间, which 是mode
*/
2,信号的屏蔽
有些时候我们希望我们的进程正常地执行,而不希望进程受到其他信号的影响,这时候我们就可以就一些信号变成进程的屏蔽信号,从而实现进程对某些信号的屏蔽。常用的函数有:
#include<signal.h> int sigemptyset(sigset_t *set) ; //初始化set集合,使set集合为空 int sigfillset(sigset_t *set); //初始化set集合,使set集合包含所有的信号 int sigaddset(sigset_t *set , int signo); //将signo信号加入到set集合中 int sigdelset(sigset_t *set , int signo); //将signo信号从set集合中删除 int sigismember(sigset_t *set , int signo) //查询信号是否在集合set中 int sigprocmask(int how , sigset_t *set , sigset_t *oset ) //将set集合中的信号加入到进程的阻塞集合中去 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中的信号的到来才能将进程唤醒。同样地,之前输入的信号也并没有丢弃,只是在进程被唤醒之后再执行。
/*
Linux信号 sigsuspend和sigaction结合使用。
*/
#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 ;
/*定义和初始化act结构*/
sigemptyset(&act.sa_mask);
act.sa_flags = 0 ;
act.sa_handler = ope ;
/*注册信号SIGINT和SIGQUIT信号的处理函数*/
sigaction(SIGINT , &act , NULL );
sigaction(SIGQUIT , &act , NULL );
printf("ls -s %d %d\n",SIGQUIT,getpid());
sigemptyset(&new_mask); /*清空new_mask*/
sigaddset(&new_mask , SIGQUIT) ; /*将SIGQUIT信号加入到 new_mask中*/
sigprocmask(SIG_BLOCK,&new_mask,&old_mask) ;
sigemptyset(&zero_mask);
while(flag == 0){
/*用一个空的set当作进程的信号屏蔽,即进程接受任何信号都将被唤醒,但是由于flag的值未改变,因此还在while中
直到一个不SIGQUIT的出现将flag变为1,进程才结束。*/
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 ;
}
}