signal是系统响应一定条件而产生的事件,接收到该信号的进程做出相应的处理。通常该字母是由错误生成的,例如段错误(SIGSEGV)。但是信件也可以用作进程间通信的一种方式,由一个进程发送到另一个进程。信号定义在signal.h文件中,以SIG开头,可以用kill-l命令查看。有关详细信息,请参阅man7信号。信号处理信号可以通过signal和sigaction函数进行注册和处理。signal函数是structsigaction中sa_handler的便捷实现。信号函数原型:void(*signal(intsig,void(*func)(int)))(int);其中sig是需要捕获的信号编号,后者是信号捕获后指向处理函数的指针,所以处理函数的原型必须是voidfunc(int),一个简单的代码示例如下:#include#include#includestaticvoidhandler(intsig){printf("接收到的信号:%d\n",sig);}intmain(intargc,char*argv[]){信号(SIGINT,处理程序);printf("捕获到SIGINT,输入'quit'退出...\n");//等待信号捕获charbuf[1024]={0};while(1){printf("请输入:");scanf("%s",缓冲器);如果(strcmp(buf,"退出")==0){中断;}}printf("退出...\n");return0;}此外,api还提供了以下两个特殊的处理函数:SIG_IGN忽略这个信号SIG_DFL恢复这个信号的默认行为sigaction函数原型:intsigaction(intsig,conststructsigaction*restrictact,structsigaction*restrict行动);其中sig为信号编号,act指定信号的处理行为,如果oact不为NULL,则返回信号之前的处理行为了structsigaction的主要成员如下:类型名称描述void(*)(int)sa_handler处理函数指针,同signal函数中的func参数sigset_tsa_mask信号掩码字,指当前阻塞的一组信号不能被当前进程接收intsa_flags处理行为修饰符,表示哪个处理函数生效,详见下文其中,sa_flags主要可以设置为以下几个值:SA_NOCLDSTOP子进程停止时不产生SIGCHLD信号SA_RESETHAND在处理函数入口处将信号的处理函数重置为SIG_DFLSA_RESTART并重启可中断函数,而不是给出EINTR错误SA_SIGINFO使用sa_sigaction作为信号处理函数SA_NODEFER不捕获信号将其添加到信号掩码字中的简单代码示例如下:#include#include#include#defineSIGSIGINTstaticvoidsig_handler(intsig,siginfo_t*si,void*data){printf("捕获信号:%d\n",sig);printf("发件人pid:%d\n",si->si_pid);printf("Senderuid:%d\n",si->si_uid);}staticintsig_caught(intsig){printf("开始捕获信号:%d\n",sig);结构sigactionsa;sa.sa_flags=SA_SIGINFO;sa.sa_sigaction=sig_handler;sigemptyset(&sa.sa_mask);整数ret=sigaction(sig,&sa,NULL);if(ret==-1){printf("捕获信号失败:%d\n",sig);返回-1;}return0;}intmain(intargc,char*argv[]){if(sig_caught(SIG)==-1){return-1;}printf("捕捉到信号(%d),输入'quit'退出...\n",SIG);字符buf[1024]={0};while(1){printf("请输入:");scanf("%s",缓冲器);如果(strcmp(buf,"退出")==0){中断;}}printf("退出...\n");return0;}signalmaskword考虑这种情况:在signal()/sigaction()返回之前,进程已经收到一个需要处理的信号。这时候进程会以默认的行为进行处理,这显然不符合我们的预期。这时候就需要用到信号掩码字了。进程启动时,将要处理的信号加入mask字,以此类推signal()/sigaction()返回后unmask,至少接收到的pending信号会发送给unmask后的进程。掩码字使用以下函数:intsigemptyset(sigset_t*set);intsigaddset(sigset_t*set,intsigno);intsigprocmask(inthow,constsigset_t*restrictset,sigset_t*restrictoset);sigprocmask中set是必需的掩码集,oset是之前的掩码集,how控制set如何生效,可以设置为如下值:SIG_BLOCK进程的掩码集将是当前的并集maskedwordset和set,set包含需要的MaskedsignalsetSIG_UNBLOCK本次处理的maskedwordset会是当前maskedwordset和set的补集的交集,set包含需要unmasked的信号集SIG_SETMASK这个过程的maskedwordset会被设置为setSimple的值设置过程如下:intsig_block(intsig,inthow){sigset_tmask;sigemptyset(&mask)sigaddset(&mask,sig);sigprocmask(how,&mask,NULL);}信号发送信号可以通过kill函数发送给指定进程,也可以通过raise或alarm函数发送给当前正在执行的线程或进程。下面分别说说这几个功能。kill原型:intkill(pid_tpid,intsig);kill函数向指定的进程发送指定的信号。如果信号为0,将执行错误检查,并且不会发送信号。它可用于检查pid的有效性。当pid大于0时,会向本进程发送信号,当pid小于等于0时,如下:等于0的信号会被发送给发送者组中的所有进程。等于-1的信号将被发送到所有进程。小于-1的信号将被发送到进程。组内所有进程pid的绝对值报警原型:unsignedalarm(unsignedseconds);闹钟函数会在指定的秒数后发送SIGALRM信号,如果秒数为0,则取消之前的定时器请求。如果不为0,则取消之前的请求,重新设置为秒。如果在等待结束之前发生了其他事件,则定时器请求也将被取消。一个简单的代码示例如下:#include#include#includestaticvoidhandler(intsig){printf("alarmarrived:%d\n",sig);}intmain(intargc,char*argv[]){signal(SIGALRM,handler);警报(2);睡觉(2);printf("报警5s结束\n");警报(10);睡觉(1);未签名的int剩余=警报(3);printf("报警10s剩余:%u,重置为3\n",剩余);睡觉(3);printf("报警3s结束\n");警报(20);睡觉(3);剩余=警报(0);printf("cancelalarm20s,remian:%u,exit...\n",remaining);}raiseprototype:intraise(intsig);raise函数会给当前正在执行的线程或进程发送一个信号。如果信号处理函数已经被调用,raise函数会等待信号处理函数调用完成再返回。结论信号处理函数会被重复调用,所以要保持可重入,注意处理逻辑。另外,这篇文章的代码是在signal里面的,这个repo还有其他的例子,有兴趣的可以看看。附件信息表/*ISOC99信号。*/#defineSIGINT2/*交互式注意信号。*/#defineSIGILL4/*非法指令。*/#defineSIGABRT6/*异常终止。*/#defineSIGFPE8/*错误的算术运算。*/#defineSIGSEGV11/*对存储的无效访问。*/#defineSIGTERM15/*终止请求。*//*POSIX指定的历史信号。*/#defineSIGHUP1/*挂断。*/#defineSIGQUIT3/*退出。*/#defineSIGTRAP5/*跟踪/断点陷阱。*/#defineSIGKILL9/*被杀死。*/#defineSIGBUS10/*总线错误。*/#defineSIGSYS12/*错误的系统调用。*/#defineSIGPIPE13/*破损的管道。*/#defineSIGALRM14/*闹钟。*//*新(更)POSIX信号(1003.1-2008、1003.1-2013)。*/#defineSIGURG16/*紧急数据在s可用火箭。*/#defineSIGSTOP17/*停止,不可阻塞。*/#defineSIGTSTP18/*键盘停止。*/#defineSIGCONT19/*继续。*/#defineSIGCHLD20/*孩子终止或停止。*/#defineSIGTTIN21/*从控制终端读取后台。*/#defineSIGTTOU22/*后台写入控制终端。*/#defineSIGPOLL23/*可轮询事件发生(系统V)。*/#defineSIGXCPU24/*超出CPU时间限制。*/#defineSIGXFSZ25/*超出文件大小限制。*/#defineSIGVTALRM26/*虚拟计时器已过期。*/#defineSIGPROF27/*分析计时器已过期。*/#defineSIGUSR130/*用户定义的信号1。*/#defineSIGUSR231/*用户定义的信号2。*//*在所有现代POSIX系统(包括BSD和Linux)中发现的非标准信号。*/#defineSIGWINCH28/*W窗口大小更改(4.3BSD,Sun)。*//*兼容性的古老名称。*/#defineSIGIOSIGPOLL/*现在可以使用I/O(4.2BSD)。*/#defineSIGIOTSIGABRT/*IOT指令,PDP-11上的abort()。*/#defineSIGCLDSIGCHLD/*旧系统V名称*//*并非所有系统都支持实时信号。bits/signum.h表示通过将__SIGRTMAX覆盖为大于__SIGRTMIN的值来支持它们。这些常量给出了内核级别的硬限制,但glibc可能会在内部使用一些实时信号。不要在应用程序代码中使用这些常量;请改用SIGRTMIN和SIGRTMAX(在signal.h中定义)。*/#define__SIGRTMIN32#define__SIGRTMAX__SIGRTMIN/*最大信号数+1(包括实时信号)。*/#define_NSIG(__SIGRTMAX+1)