信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念、Linux对信号机制的大致实现方法、如何使用信号,以及有关信号的几个系统调用。信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。从它的命名可以看出,它的实质和使用很象中断。所以,信号可以说是进程控制的一部分。一、信号的基本概念本节先介绍信号的一些基本概念,然后给出一些基本的信号类型和信号对应的事件。基本概念对于理解和使用信号,对于理解信号机制都特别重要。下面就来看看什么是信号。1、基本概念软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。2、信号的类型发出信号的原因很多,这里按发出信号的原因简单分类,以了解各种信号:(1)与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。(2)与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。(3)与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。(4)与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。(5)在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号。(6)与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。(7)跟踪进程执行的信号。Linux支持的信号列表如下。很多信号是与机器的体系结构相关的,首先列出的是POSIX.1中列出的信号:信号值处理动作发出信号的原因----------------------------------------------------------------------SIGHUP1A终端挂起或者控制进程终止SIGINT2A键盘中断(如break键被按下)SIGQUIT3C键盘的退出键被按下SIGILL4C非法指令SIGABRT6C由abort(3)发出的退出指令SIGFPE8C浮点异常SIGKILL9AEFKill信号SIGSEGV11C无效的内存引用SIGPIPE13A管道破裂:写一个没有读端口的管道SIGALRM14A由alarm(2)发出的信号SIGTERM15A终止信号SIGUSR130,10,16A用户自定义信号1SIGUSR231,12,17A用户自定义信号2SIGCHLD20,17,18B子进程结束信号SIGCONT19,18,25进程继续(曾被停止的进程)SIGSTOP17,19,23DEF终止进程SIGTSTP18,20,24D控制终端(tty)上按下停止键SIGTTIN21,21,26D后台进程企图从控制终端读SIGTTOU22,22,27D后台进程企图从控制终端写下面的信号没在POSIX.1中列出,而在SUSv2列出信号值处理动作发出信号的原因--------------------------------------------------------------------SIGBUS10,7,10C总线错误(错误的内存访问)SIGPOLLASysV定义的Pollable事件,与SIGIO同义SIGPROF27,27,29AProfiling定时器到SIGSYS12,-,12C无效的系统调用(SVID)SIGTRAP5C跟踪/断点捕获SIGURG16,23,21BSocket出现紧急条件(4.2BSD)SIGVTALRM26,26,28A实际时间报警时钟信号(4.2BSD)SIGXCPU24,24,30C超出设定的CPU时间限制(4.2BSD)SIGXFSZ25,25,31C超出设定的文件大小限制(4.2BSD)(对于SIGSYS,SIGXCPU,SIGXFSZ,以及某些机器体系结构下的SIGBUS,Linux缺省的动作是A(terminate),SUSv2是C(terminateanddumpcore))。下面是其它的一些信号信号值处理动作发出信号的原因----------------------------------------------------------------------SIGIOT6CIO捕获指令,与SIGABRT同义SIGEMT7,-,7SIGSTKFLT-,16,-A协处理器堆栈错误SIGIO23,29,22A某I/O操作现在可以进行了(4.2BSD)SIGCLD-,-,18A与SIGCHLD同义SIGPWR29,30,19A电源故障(SystemV)SIGINFO29,-,-A与SIGPWR同义SIGLOST-,-,-A文件锁丢失SIGWINCH28,28,20B窗口大小改变(4.3BSD,Sun)SIGUNUSED-,31,-A未使用的信号(willbeSIGSYS)(在这里,-表示信号没有实现;有三个值给出的含义为,第一个值通常在Alpha和Sparc上有效,中间的值对应i386和ppc以及sh,最后一个值对应mips。信号29在Alpha上为SIGINFO/SIGPWR,在Sparc上为SIGLOST。)处理动作一项中的字母含义如下A缺省的动作是终止进程B缺省的动作是忽略此信号C缺省的动作是终止进程并进行内核映像转储(dumpcore)D缺省的动作是停止进程E信号不能被捕获F信号不能被忽略上面介绍的信号是常见系统所支持的。以表格的形式介绍了各种信号的名称、作用及其在默认情况下的处理动作。各种默认处理动作的含义是:终止程序是指进程退出;忽略该信号是将该信号丢弃,不做处理;停止程序是指程序挂起,进入停止状况以后还能重新进行下去,一般是在调试的过程中(例如ptrace系统调用);内核映像转储是指将进程数据在内存的映像和进程在内核结构中存储的部分内容以一定格式转储到文件系统,并且进程退出执行,这样做的好处是为程序员提供了方便,使得他们可以得到进程当时执行时的数据值,允许他们确定转储的原因,并且可以调试他们的程序。注意信号SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。信号SIGIOT与SIGABRT是一个信号。可以看出,同一个信号在不同的系统中值可能不一样,所以建议最好使用为信号定义的名字,而不要直接使用信号的值。三、有关信号的系统调用前面两节已经介绍了有关信号的大部分知识。这一节我们来了解一下这些系统调用。其中,系统调用signal是进程用来设定某个信号的处理方法,系统调用kill是用来发送信号给指定进程的。这两个调用可以形成信号的基本操作。后两个调用pause和alarm是通过信号实现的进程暂停和定时器,调用alarm是通过信号通知进程定时器到时。所以在这里,我们还要介绍这两个调用。1、signal系统调用系统调用signal用来设定某个信号的处理方法。该调用声明的格式如下:void(*signal(intsignum,void(*handler)(int)))(int);在使用该调用的进程中加入以下头文件:#includesignal.h上述声明格式比较复杂,如果不清楚如何使用,也可以通过下面这种类型定义的格式来使用(POSIX的定义):typedefvoid(*sighandler_t)(int);sighandler_tsignal(intsignum,sighandler_thandler);但这种格式在不同的系统中有不同的类型定义,所以要使用这种格式,最好还是参考一下联机手册。在调用中,参数signum指出要设置处理方法的信号。第二个参数handler是一个处理函数,或者是SIG_IGN:忽略参数signum所指的信号。SIG_DFL:恢复参数signum所指信号的处理方法为默认值。传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。系统调用signal返回值是指定信号signum前一次的处理例程或者错误时返回错误代码SIG_ERR。下面来看一个简单的例子:#includesignal.h#includeunistd.h#includestdio.hvoidsigroutine(intdunno){/*信号处理例程,其中dunno将会得到信号的值*/switch(dunno){case1:printf(Getasignal--SIGHUP);break;case2:printf(Getasignal--SIGINT);break;case3:printf(Getasignal--SIGQUIT);break;}return;}intmain(){printf(processidis%d,getpid());signal(SIGHUP,sigroutine);//*下面设置三个信号的处理方法signal(SIGINT,sigroutine);signal(SIGQUIT,sigroutine);for(;;);}其中信号SIGINT由按下Ctrl-C发出,信号SIGQUIT由按下Ctrl-发出。该程序执行的结果如下:localhost:~$./sig_testprocessidis463Getasignal-SIGINT//按下Ctrl-C得到的结果Getasignal-SIGQUIT//按下Ctrl-得到的结果//按下Ctrl-z将进程置于后台[1]+Stopped./sig_testlocalhost:~$bg[1]+./sig_test&localhost:~$kill-HUP463//向进程发送SIGHUP信号localhost:~$Getasignal–SIGHUPkill-9463//向进程发送SIGKILL信号,终止进程localhost:~$2、kill系统调用系统调用kill用来向进程发送一个信号。该调用声明的格式如下:intkill(pid_tpid,intsig);在使用该调用的进程中加入以下头文件:#includesys/types.h#includesignal.h该系统调用可以用来向任何进程或进程组发送任何信号。如果参数pid是正数,那么该调用将信号sig发送到进程号为pid的进程。如果pid等于0,那么信号sig将发送给当前进程所属进程组里的所有进程。如果参数pid等于-1,信号sig将发送给除了进程1和自身以外的所有进程。如果参数pid小于-1,信号sig将发送给属于进程组-pid的所有进程。如果参数sig为0,将不发送信号。该调用执行成功时,返回值为0;错误时,返回-1,并设置相应的错误代码errno。下面是一些可能返回的错误代码:EINVAL:指定的信号sig无效。ESRCH:参数pid指定的进程或进程组不存在。注意,在进程表项中存在的进程,可能是一个还没有被wait收回,但已经终止执行的僵死进程。EPERM:进程没有权力将这个信号发送到指定接收信号的进程。因为,一个进程被允许将信号发送到进程pid时,必须拥有root权力,或者是发出调用的进程的UID或EUID与指定接收的进程的UID或保存用户ID(savedset-user-ID)相同。如果参数pid小于-1,即该信号发送给一个组,则该错误表示组中有