发信人:scircle(yuanyuan),信区:Security标题:unix环境高级编程--第10章信号(下)发信站:BBS水木清华站(MonMar2716:03:152000)〓sigprocmask函数在节中曾说明一个进程的信号屏蔽字规定了当前阻塞而不能传送给该进程的信号集。调用函数sigprocmask可以检测或更改(或两者)进程的信号屏蔽字。intsigprocmask(inthow,constsigset迹茫模*常病絫*set,sigset迹茫模?2〗t*返回:若成功为0,出错为-1Returns:0ifOK,-1on首先,oset是非定指针,进程的当前信号屏蔽字通过oset返回。其次,若set是一个非定指针,则参数how指示如何修改当前信号屏蔽字。图说明了how可选用的值。SIG-BLOCK是或操作,而SIG-SETMASK则是赋值操作。〖HT5SS〗图1用sigprocmask更改当前信号屏蔽字的方法〖HT5〗如果set是个定指针,则不改变该进程的信号屏蔽字,how的值也无意义。如果在调用sigprocmask后有任何未决的,不再阻塞的信号,则在sigprocmask返回的至少将其中之一传送给该进程。实例程序是一个函数,它打印调用进程的信号屏蔽字所阻塞的信号的名称。从程序和中调用此函数。为了节省空间,没有对图中列出的每一种信号测试该屏蔽字。(见练习程序为进程打印信号帘为?〓sigpending函数sigpending返回对于调用进程被阻塞不能传送和当前未决的信号集。该信号集通过set参数返回。intsigpending(sigset迹茫模*常病絫返回:若成功为0,出错为-实例程序使用了很多前面说明过的信号功能。进程阻塞了SIGQUIT信号,保存了当前信号屏蔽字(以便以后恢复),然后睡眠5秒钟。在此期间所产生的退出信号都被阻塞,不传送至该进程,直到该信号不再被阻塞。在5秒睡眠结束后,检查是否有信号未决,然后将SIGQUIT设置为不再阻塞。注意,在设置SIGQUIT为阻塞时,我们保存了老的屏蔽字。为了解除对该信号的阻塞,我们用老的屏蔽字重新设置了进程信号屏蔽字(SIG-SETMAS)。可以使用的另一种方法是用SIG-UNBLOCK使以前阻塞的信号不再阻塞。但是,应当了解如果我们编写了一个可能由其他人使用的函数,而且我们需要在我们的函数中阻塞一个信号,则不能用SIG-UNBLOCK解除对此信号的阻塞,这是因为此函数的调用者在调用本函数之前可能也阻塞了此信号。在这种情况下我们必须使用SIG-SETMASK将信号屏蔽字恢复为原先值。在节的system函数部分有这样的一个例子。在睡眠期间如果产生了退出信号,那么程序运行到这一点则该信号是未决的,但是不再受阻塞,所以在sigprocmask返回之前,它就被传送到本进程。从程序的输出中我们可以看到这一点:在SIGQUIT处理程序(sig-quit)中的printf语句先执行,然后再执行sigprocmask之后的printf语句。〖HT5〗程序信号设置和sigprocmask的实例〖HT5〗然后该进程再睡眠5秒钟。如果在此期间再产生退出信号,那么它就会使该进程终止,因为在上次捕捉到该信号时,已将其处理方式设置为默认动作。此时如果键入终端退出字符Control-\,则输出QUIT(coredump)信息,表示进程因接到SIGQUIT而终止,但是在core文件中保存了与进程有关的信息(该信息是由shell发现其子进程异常终止时打印的。$^\产生信号一次(在5秒之内SIGQUITpending从sleep返回后caughtSIGQUIT在信号处理程序中SIGQUITunblocked从sigprocmask返回后^\Quit(coredump)再次产生信号$^\^\^\^\^\^\^\^\^\^\产生信号10次(在5秒之内SIGQUITcaughtSIGQUIT只产生信号一次SIGQUIT^\Quit(coredump)再产生信号注意,在第二次运行该程序时,在进程睡眠期间我们使SIGQUIT信号产生了10次,但是解除了对该信号的阻塞后,只向进程传送一次SIGQUIT。从中可以看出在此系统上没有将信号进行排队。〓sigaction函数sigaction函数的功能是检查或修改(或两者)与指定信号相关联的处理动作。此函数取代了Unix早期版本使用的signal函数。在本市末尾我们用sigaction函数实现了signal。intsigaction(intsigno,conststructsigactionstructsigaction返回:若成功为0,出错为-其中,参数signo是要检测或修改具体动作的信号的编号数。若act指针非定,则要修改其动作。如果oact指针非定,则系统返回该信号的原先动作。此函数使用下列结构:structsigactionvoid(*sa迹茫模*常病絟andler)();/*信号处理程序的地址,或SIG-IGN,或SIG-DFL,sigset迹茫模*常病絫sa迹茫模*常病絤ask;/*添加的要阻塞的信号intsa迹茫模*常病絝lags;/*信号选项,见图当更改信号动作时,如果sa-handler指向一个信号捕捉函数(不是常数SIG-IGN或SIG-DFL),则sa-mask字段说明了一个信号集,在调用信号捕捉函数之前,该信号集要加到进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字恢复为原先值。这样,在调用执行信号处理程序时就能阻塞某些信号。在信号处理程序被调用时,系统建立的新信号屏蔽字会自动包括正被传送的信号。因此就保证了在处理一个给定的信号时,如果这种信号再次发生,那么它会被阻塞到对前一个信号的处理结束为止。回忆节,若同一种信号多次发生,通常并不将它们排队,所以如果在某种信号被阻塞时,它发生了五次,那么对这种信号解除阻塞后,其信号处理函数通常只会被调用一次。一旦对一个给定的信号设置了一个动作,那么在用sigaction改变它之前,该设置就一直有效。这与早期的不可靠信号机制不同,而符合了在这方面的要求。act结构的sa-flags字段包含了对信号进行处理的各个选择项。图详细列出了这些选择项的意义。图信号处理的选择项标志(sa-实例〖CD2〗signal函数现在让我们用sigaction实现signal函数。也是这样做的的原理阐述中分也说明这里POSIX所希望的)。SVR4则提供老的不可靠信号语义的signal函数。除非为了向后兼容面使用老的语义,在SVR4之下,你也应使用下面的signal实现,或者直接调用sigaction(在SVR4下,可以在调用sigaction时指定SA-RESETHAND和SA-NODEFER选项以实现老的语义的signal函数)。在本书中所有调用signal的实例都是调用程序中所实现的该函数。〖HT5〗程序用sigaction所实现的signal函数〖HT5〗注意,我们必须用sigemptyset函数初始化act结构的成员。不能保证-会做同样的事情。我们对除SIGALRM以外的所有信号都企图设置SA-RESTART标志,于是被这些信号中断的系统调用都能再起动。不希望再起动由SIGALRM信号中断的系统调用的原因是:我们希望对I/O操作可以设置时间限制。(请回忆与程序有关的讨论。某些系统定义了SA-INTERRUPT标志。这些系统的默认方式是重新起动被中断的系统调用,而指定此标志则使系统调用被中断后不再重起动。实例〖CD2〗signal迹茫模*常病絠ntr函数程序是signal函数的另一种版本,它阻止被中断的系统调用的再起动。程序signal-intr函数如果系统定义了SA-INTERRUPT,则在sa-flags中增加该标志,这样也就阻止了被中断的系统调用的再起动。〓sigsetjmp和siglongjmp函数在节中说明了用于非局部转移的setjmp和longjmp函数。在信号处理程序中经常调用longjmp函数以返回到程序的主循环中,而不是从该处理程序返回。确实,ANSIC标准说明一个信号处理程序可以或者返回,或者调用abort,exit或longjmp。在程序和8中已经有了这种情况。调用longjmp有一个问题。当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动地加到进程的信号屏蔽字中。这阻止了后来产生的这种信号中断此信号处理程序。如果用longjmp跳出此信号处理程序,则对此进程的信号屏蔽字会发生什么呢在下,setjmp和longjmp保存和恢复信号屏蔽字。但是,SVR4并不做这种操作。3+BSD提供函数-setjmp和-longjmp,它们也不保存和恢复信号屏蔽字。为了允许两种形式并存,并没有说明setjmp和longjmp对信号屏蔽字的作用。作为替代,定义了两个新函数sigsetjmp和siglongjmp。在信号处理程序中作非局部转移时应当使用这两个函数。#includeintsigsetjmp(sigjmp迹茫模*常病絙ufenv,intsvoidsiglongjmpint;返回:若直接调用为0,若从siglongjmp调用返回则为非0。这两个函数和setjmp,longjmp之间的唯一区别是sigsetjmp增加了一个参数。如果savemask非0,则sigsetjmp在env中保存进程的当前信号屏蔽字。在调用siylongjmp时,如果带非0savemask的sigsetjmp调用已经保存了env,则siglongjmp从其中恢复保存的信号屏蔽字。实例程序显示了在信号处理程序被调用时,系统所设置的信号屏蔽字如何自动地包括刚被捕捉到的信号。它也例示了如何使用sigsetjmp和siglongjmp函数。程序信号屏蔽、sigsetjmp和siglongjmp的实例此程序例示了另一种技术,只要在信号处理程序中调用siglongjmp就应使用这种技术。在调用sigsetjmp之后将变量canjump设置为非0。在信号处理程序中检测此变量,仅当它为非0值时才调用siglongjmp。这提供了一种保护机制,使得若在jmpbuf(跳转缓存)尚未由sigsetjmp初始化时,调用信号处理程序,则不执行其处理动作就返回。(在本程序中,siglongjmp之后程序很快就结束,但是在较大的程序中,在siglongjmp之后,信号处理程序可能仍旧被设置)。在一般的C代码中(不是信号处理程序),对于longjmp并不需要这种保护措施。但是,因为信号可能在任何时候发生,所以在信号处理程序中,需要这种保获措施。在程序中使用了数据类型sig-atomic-c,它是ANSIC定义的在写时不会被中断的变量类型。它意味着这种变量在具有虚存的系统上不会跨越页的边界,可以用一条机器指令对其进行存取。对于这种类型的变量总是带ANSIC的类型修饰符vlatile,其原因是:该变量将由二个不同的控制线-main函数和异步执行的信号处理程序存取。图显示了此程序的执行时间顺序。可将图分成三部分:左面部分(对应于main),中间部分(sig-usr1)和右面部分(sig-alrm)。在进程执行左面部分时,信号屏蔽字是0(没有信号是阻塞的)。而执行中间部分时,其信号屏蔽字是SIGUSR1。执行右面部分时,信号屏蔽字是SIGUSR1|SIGALRM。〖HT5〗图处理两个信号的实例程序的时间顺序〖HT5〗执行程序得到下面的输出:$&在后台启动进程starting[1]531作业控制shell打印其进程$kill-USR1531向该进程发送startingsig迹茫模*常病絬$insig迹茫模*常病絘lrm:SIGUSR1finishingsig迹茫模*常病絬ending[1]+Done&键入回车这与我们所期望的相同:当调用一个信号处理程序时,被捕捉到的信号加到了进程的当前信号屏蔽字。当从信号处理程序返回时,原来的屏蔽字被恢复。另外,siglongjmp恢复了由sigsetjmp所保存的信号屏蔽字。如果将程序中的sigsetjmp和sig