1.信号:顾名思义,信号就是来传递信息的,传递的对象为进程(可用于进程间的通信);可由硬件或软件(使用kill、raise等)产生;终端下使用 kill -l 可以显示linux系统支持的所有信号;信号分为可靠信号和不可靠信号,不会丢失支持排队的为可靠信号,不可靠则非之。
2.信号的处理:
第一种:信号的捕捉;调用的API有signal & sigaction,安装信号处理函数function;
格式:signal(int sig, function) ,sig为信号编号,function为信号处理函数,错误发生时返回SIG_ERR.
sigaction(int sig, const struct sigaction &act, struct &oldact);
struct sigaction{
void(*sa_handler) (int); //sa_handler可以为SIG_DFL,SIG_IGN或者信号处理函数名
void(sa_sigaction)(int,siginfo_t ,void*);
sigset_t sa_mask; //信号屏蔽集
int sa_flags; //信号处理的一些相关操作
void(*sa_restorer)(void);//废弃不用
}
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
int tmp =0;
void handle_signal(int number)
{
printf("receive signal\n");
sleep(5);
tmp += 1;
printf("the value of tmp is:%d\n",tmp);
printf("in handle_signal,after sleep\n");
}
int main(int argc,char **argv)
{
struct sigaction act;
act.sa_handler = handle_signal;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
/*安装信号处理函数*/
sigaction(SIGINT,&act,NULL);
while(1)
{
;
}
return 0;
}
第二种:信号的忽略,对应的信号处理函数为常量SIG_IGN,然而信号编号为SIGKILL & SIGSTOP时不能被忽略、捕获、堵塞。
第三种:信号的默认处理。大部分信号的默认操作就是终止进程。
3.信号处理函数的非局部跳转
goto用于函数内部的局部跳转,而处理函数的跳转需要用到setjmp/longjmp or sigsetjmp/siglongjmp 这对组合。
格式:void longjmp(jmp_buf env,int val) / int setjmp(jmp_buf env) ;
void siglongjmp(sigjmp_buf env, int val) / int sigsetjmp(sigjmp_buf env, int savesigs);
二者的相同之处:程序处理时先调用setjmp/sigsetjmp,使用env保存进程的当前的信号屏蔽集,并返回0
二者的区别:信号处理时会自动阻塞正在被处理的信号,将处理的信号加入到屏蔽集中,在信号处理函数返回时把进程的信号屏蔽字恢复,即取消对当前信号的阻塞。前者使用longjmp直接跳转,未让处理函数正常终止,会造成当前信号仍旧是阻塞,无法再次启动信号处理函数,需要手动清除屏蔽信号; 而后者中sigsetjmp多了一个参数savesigs,当savesigs非0时,sigsetjmp中env中保存进程的当前信号的屏蔽集,在调用siglongjmp时会从中恢复保存的信号屏蔽集,从而完成跳传后对处理信号的解除阻塞。
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>
static sigjmp_buf jmpbuf;
static void handle_signal( int signo)
{
ptintf("\nSIGINT\n");
sleep(1);
siglongjmp(jmpbuf,1);
}
int main( int argc,char **argv)
{
sleep(5);
signal(SIGINT,handle_signal);
if(sigsetjmp(jmpbuf,4) == 1)
{
printf("I am jumped\n");
}
else
{
//raise(SIGQUIT);
printf("I am waitting signal\n");
pause();
}
return 0;
}
运行结果:I am waitting signal
终端中断
SIGINT
I am jumped
I am here
4.信号的发送
一,int kill(pid_t pid, int signo);执行成功返回值0,失败返回-1,向特定进程发送信号.亦可用命令行 kill -s signo pid
二, int raise(int signo);向调用它的进程发送信号。成功返回0,失败返回非0值;
三, int sigqueue(pid_t pid,int signo, const union sigval alue);向指定进程发送带参数的信号,一般与sigaction(设置对应位)配合使用;执行成功返回值0,失败返回-1;
四,unsigned int alarm(unsigned int seconds),定时器的作用,定时发送SIGALARM信号给调用它的进程。
五,void abort(void),给调用它的进程发送SIGABRT信号
六,int setitimer(int which ,const struct itimerval *value, struct itimerval *ovalue),功能比alarm更全面。
5.信号的屏蔽
信号集的类型:信号总数64个,不能用整型的(通常32位)一位代表一种信号,采用新的数据类型sigset_t;
信号集处理的系统调用: int sigemptyset(sigset_t *set),清空一个信号集,使其不包含任何信号。
int sigaddset(sigset_t *set,int signo),将signo添加到信号集set中。
int sigdelset(sigset_t *set,int signo),将signo从set中删除;成功返回0,失败返回-1,以上类似。
int sigismember(sigset_t *set,int signo),判断信号signo是否在信号集set中,返回1表在,0表示不在,错误返回-1;
信号的屏蔽: 每一个进程都有进程屏蔽集,它规定当前当前阻塞不能递送给进程的信号集;调用系统函数可以改变或者检查进程的信号集。
一,int sigprocmask( int how,sigset_t *set,sigset_t *oldset),其中oldset用于保存以前的屏蔽集,how可取SIG_BLOCK,SIG_UNBLOC,SIG_SETMASK(更改进程的屏蔽集),执行成功返回值0,失败返回-1;
二,int sigpending(sigset_t *set),获取调用进程因被阻塞而不能递送 和 未决排队的信号集,并保存在set中,执行成功返回值0,失败返回-1
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
static void sig_quit(int);
int main(void)
{
sigset_t newmask, oldmask, pendingmask;
/* 安装信号处理函数*/
if (signal(SIGQUIT, sig_quit) == SIG_ERR)
{
fprintf(stderr, "can't catch SIGQUIT/n");
exit(1);
}
/* 初始化信号集*/
sigemptyset(&newmask);
/* 添加指定信号*/*/
sigaddset(&newmask, SIGINT);
/* 设置新的屏蔽信号集,并记录旧的屏蔽信号集*/
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
{
fprintf(stderr, "SIG_BLOCK error/n");
exit(1);
}
sleep(5);
//获取屏蔽或未决的信号集
if (sigpending(&pendingmask) < 0)
{
fprintf(stderr, "sigpending error/n");
exit(1);
}
//判断信号是否在屏蔽码中
if (sigismember(&pendingmask, SIGINT))
printf("/nSIGQUIT pending/n");
//设置为旧的屏蔽集,即解绑信号,如果有信号,会立即进入信号处理函数中。
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
{
fprintf(stderr, "SIG_SETMASK error/n");
exit(1);
}
printf("SIGQUIT unblocked/n");
sleep(5);
exit(0);
}
static void sig_quit(int signo)
{
printf("caught SIGQUIT/n");
//恢复进程对信号的默认处理
if (signal(SIGINT, SIG_DFL) == SIG_ERR)
{
fprintf(stderr, "can't reset SIGQUIT/n");
exit(0);
}
}
六,函数sigsuspend 与pause的区别和联系:
函数原型; int sigsuspend( const sigset_t *set),总是返回-1,并将errno置为EINTR
相同点:将进程挂起,直至信号到来;信号处理函数结束后,函数返回并执行下一步操作;
区别: sigsuspend = pause + signo(指定的屏蔽信号) ;若set集为空则任何信号都可使信号处理函数作用,否则,屏蔽信号以外的信号才能使起工作。此外,sigsuspend返回后,将进程的信号屏蔽码恢复为以前的屏蔽码。
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig) //信号处理函数
{
if(sig == SIGINT)
printf("\nSIGINT sig\n");
else if(sig == SIGQUIT)
printf("\nSIGQUIT sig\n");
else
printf("\nSIGUSR1 sig\n");
}
int main()
{
sigset_t new,old,wait; //三个信号集
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0); //安装信号处理函数,信号分别为SIGINT/SIGQUIT/SIGUSR1
sigaction(SIGQUIT, &act, 0);
sigaction(SIGUSR1, &act, 0);
sigemptyset(&new);
sigaddset(&new, SIGINT); //SIGINT信号的添加
sigemptyset(&wait);
sigaddset(&wait, SIGUSR1); //SIGUSR1信号添加到wait
sigprocmask(SIG_BLOCK, &new, &old); //设置new为新的信号屏蔽集,并保存旧的屏蔽信号集
/* 总是返回-1 */
if(sigsuspend(&wait) != -1) /*等待SIGUSR1以外的信号到来,执行信号处理函数;若信号之前到来,则执行时会立即
处理被阻塞不能递送和当前未决的信号*/
printf("\nsigsuspend error\n");
printf("After sigsuspend");
sigprocmask(SIG_SETMASK, &old, NULL);/* 恢复进程的屏蔽集*/
pause();
printf("for test\n");
return 0;
}