网络推荐

推荐给好友 上一篇 | 下一篇

第四章: 高级进程控制和信号

翻译:gvim@chinaunix/bsd
:R&W:Gs%WCk&["U
一些说明:BSD爱好者乐园 di7M*}3N ebb(~S
1 很高兴有这个机会参与到这项活动中来。BSD爱好者乐园:Tz1k0`g7h${+^V
2 由于英语语言的习惯,文中有不少复杂长句,按照字面翻译出来并不适合中国人的阅读习惯。我在保留文章原意的基础上作了一些语言组织上的调整,将大部分长句子组织成中文擅长的短句型,所以你会发现翻译品一部分内容不是按照原文字面翻译的。BSD爱好者乐园T6w p\7kW C| }/b+Q
3 考虑到该书的入门引导作用,在原文中一些比较少见的词后面我添加了少许译注,希望减少入门兄弟的负担。BSD爱好者乐园w4Rq5n z`
4 由于2中的语言调整和3中的译注,是我的个人行为,可能会有我对 原文理解模糊,概念不清,或是语言组织不顺畅的地方,还请大家一定多多指出来。谢谢。
vJ] mb7HT }"{个人声明:BSD爱好者乐园G lu4G-SP W
本翻译品受控于chinaunix BSD翻译小组。如需单独转载,请保留“翻译:gvim@chinaunix/bsd”和以上几点说明。

6p.a1P!]"LJ#V(y4.1 高级进程控制和信号BSD爱好者乐园-w#r.J9I)T*h2wU ^:p
信号:4 a:一种对象,用于传输或负载人类声音之外的信息。BSD爱好者乐园g9N$\ L+p v*I(m
BSD爱好者乐园i"| U:x;u?1e
到目前为止,我们已经讨论了进程的创建和其他系统调用。现在是讨论下面这些问题的时候了:你要在多个进程间通讯以获得更好的进程控制粒度,或者要其他程序或操作者用信号通知你的程序。例如,你可能希望你的程序重新读取它的配置文件。或者,你的数据库程序需要在退出之前将事务从主存写入后备存储器,然后再退出。这两个例子可能只是使用信号的很小一部分。虽然已经有套接字,先入先出队列,管道,信号量等多种方式来完成类似的任务,但是我们将把讨论的焦点放在信号和其它进程控制机制上。在现实中,信号和进程控制机制可以提供大部分你所需要的特性和功能。
Nbs r&DBSD爱好者乐园]LD;daa|&`$h"j
4.2 信号
W2L/li;M ~#`*?信号与硬件中断很相似。当设备需要中断服务的时候它可以产生一个硬件中断来通知CPU。与硬件中断类似的,当进程需要将一些事件通知给其他进程的时候可以使用信号来完成。BSD爱好者乐园ou m`#aX0k
BSD爱好者乐园o#O2`W3wT3^yb5@
大多数Unix系统管理员会比较熟悉SIGHUP信号。当你通过kill命令向后台服务进程发出SIGHUP信号后,大多数进程要么重新读入他们的配置文件要么重新启动。这些信号之中,一些与硬件有直接关系,如SIGFPE(浮点异常),SIGILL(非法指令);其它则是与软件相关,如SIGSYS(未实现的系统调用被调用)。
(z$rJ M3dg|BSD爱好者乐园;f @xH?4} Qd
一旦进程接收到信号之后,该信号的行为与信号本身和进程对它的使用目的两个因素有关。一些信号可以被阻塞,忽略,或者捕获,而另外一些则不可以。如果进程需要捕获一个信号并履行一些相关操作,你可以为进程设定这个特定信号的信号处理句柄。处理句柄仅仅是一个函数,在这个信号被进程接收之后调用。或者更确切的说,处理句柄是一个函数调用,你可以对它进行指派(specify)。
5Lw~/Hie
y G)`N RM+i当信号没有指定处理句柄时,将会执行操作系统默认的行为。这些缺省行为可以是从终止进程到完全核心转储等不同的操作。注意,有两个信号不能被捕获或忽略:SIGSTOP和SIGKILL,下面会解释。
E5H(py?LBSD爱好者乐园V"I ?)Fr9{'u6sk5U
在BSD系统中定义的有许多信号;我们讨论在/usr/include/sys/signals.h(译注:在我的FB5.2.1中是signal.h)文件中定义的标准信号。注意, NetBSD系统中定义的信号数量稍微多一点,并且我们没有将它们的讨论放在这里。所以如果需要使用某个下面没有涉及到的信号的时候,请查阅你的系统的头文件
*b4G#{6o nu3I1j p8YBSD爱好者乐园^Hu#PP2Bx Ttv K
 
#define SIGHUP          1       /* hangup */
BSD爱好者乐园.@6FR mP0H/o }t
SIGHUP是Unix系统管理员很常用的一个信号。许多后台服务进程在接受到该信号后将会重新读取它们的配置文件。然而,该信号的实际功能是通知进程它的控制终端被断开。缺省行为是终止进程。
WTC#xj;R9eJmBSD爱好者乐园W&e)g9W0?zh.}/t5Y9b
 
#define SIGINT          2       /* interrupt */
BSD爱好者乐园%e5C0M ?!U#mN
对于Unix使用者来说,SIGINT是另外一个常用的信号。许多shell的CTRL-C组合使得这个信号被大家所熟知。该信号的正式名字是中断信号。缺省行为是终止进程。BSD爱好者乐园T/u!~Xw ~ ss|9N{3B

;r_~)Bmf
 
#define SIGQUIT         3       /* quit */
BSD爱好者乐园 ] Upm${)E8XL9j
SIGQUIT信号被用于接收shell的CTRL-/组合。另外,它还用于告知进程退出。这是一个常用信号,用来通知应用程序从容的(译注:即在结束前执行一些退出动作)关闭。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园5Tfj0p"?

K X\ ?X.Z+\;Qe
 
#define SIGILL          4       /* illegal instr. (not reset when caught) */

8V$B+S'h"W ?如果正在执行的进程中包含非法指令,操作系统将向该进程发送SIGILL信号。如果你的程序使用了线程,或者pointer functions,那么可能的话可以尝试捕获该信号来协助调试。(注意:原文这句为:“If your program makes use of use of threads, or pointer functions, try to catch this signal if possible for aid in debugging.”。中间的两个use of use of,不知是原书排版的瑕疵还是我确实没有明白其意义;另外,偶经常听说functions pointer,对于pointer functions,google了一下,应该是fortran里面的东西,不管怎样,还真不知道,确切含义还请知道的兄弟斧正。)缺省行为是终止进程,并且创建一个核心转储。
$pa,\@Q!m
yQ`5g7Y(|
 
#define SIGTRAP         5       /* trace trap (not reset when caught) */
BSD爱好者乐园 v2EBP,f o#[
SIGTRAP这个信号是由POSIX标准定义的,用于调试目的。当被调试进程接收到该信号时,就意味着它到达了某一个调试断点。一旦这个信号被交付,被调试的进程就会停止,并且它的父进程将接到通知。缺省行为是终止进程,并且创建一个核心转储。
&V*C'r;_Y'n_ M/s9{
2_6~qu?
 
#define SIGABRT         6       /* abort() */
BSD爱好者乐园A,AF2b8K syg
SIGABRT提供了一种在异常终止(abort)一个进程的同时创建一个核心转储的方法。然而如果该信号被捕获,并且信号处理句柄没有返回,那么进程不会终止。缺省行为是终止进程,并且创建一个核心转储。
1rH&?k X5}7G({9k7~N*`
U0dIIqVQ
 
#define SIGFPE          8       /* floating point exception */

rU7Uw[[9@}当进程发生一个浮点错误时,SIGFPE信号被发送给该进程。对于那些处理复杂数学运算的程序,一般会建议你捕获该信号。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园-\4fBp GU [

v,XxL9DbR{
 
#define SIGKILL         9       /* kill (cannot be caught or ignored) */
BSD爱好者乐园"E.N?#N$E!TLb3k^
SIGKILL是这些信号中最难对付的一个。正如你在它旁边的注释中看到的那样,这个信号不能被捕获或忽略。一旦该信号被交付给一个进程,那么这个进程就会终止。然而,会有一些极少数情况SIGKILL不会终止进程。这些罕见的情形在处理一个“非中断操作”(比如磁盘I/O)的时候发生。虽然这样的情形极少发生,然而一旦发生的话,会造成进程死锁。唯一结束进程的办法就只有重新启动了。缺省行为是终止进程。BSD爱好者乐园2\d5k Cb k"w}A
BSD爱好者乐园}x%r U9vQS-yM6s
 
#define SIGBUS          10      /* bus error */
BSD爱好者乐园oxi@ n
如同它的名字暗示的那样,CPU检测到数据总线上的错误时将产生SIGBUS信号。当程序尝试去访问一个没有正确对齐的内存地址时就会产生该信号。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园.d&v3a*LC0]{s_
BSD爱好者乐园:i5l$q3f#@Z.V*o'o
 
#define SIGSEGV         11      /* segmentation violation */

(@ S]G[SIGSEGV是另一个C/C++程序员很熟悉的信号。当程序没有权利访问一个受保护的内存地址时,或者访问无效的虚拟内存地址(脏指针,dirty pointers,译注:由于没有和后备存储器中内容进行同步而造成。关于野指针,可以参见http://en.wikipedia.org/wiki/Wild_pointer 的解释。)时,会产生这个信号。缺省行为是终止进程,并且创建一个核心转储。
*X$V+a'L}Zf sK6tqBSD爱好者乐园&At"et#Ber'j$lD g!e
 
#define SIGSYS          12      /* non-existent system call invoked */
BSD爱好者乐园5ETmS%b8V"r
SIGSYS信号会在进程执行一个不存在的系统调用时被交付。操作系统会交付该信号,并且进程会被终止。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园\T/m?8u7g;e4Q

~j.[ cjR1p2a%q(]
 
#define SIGPIPE         13      /* write on a pipe with no one to read it */
BSD爱好者乐园C|(zl7](vL
管道的作用就像电话一样,允许进程之间的通信。如果进程尝试对管道执行写操作,然而管道的另一边却没有回应者时,操作系统会将SIGPIPE信号交付给这个讨厌的进程(这里就是那个打算写入的进程)。缺省行为是终止进程。
CoX&n1?rZ
 
#define SIGALRM         14      /* alarm clock */
BSD爱好者乐园l\r o9XV
在进程的计时器到期的时候,SIGALRM信号会被交付(delivered)给进程。这些计时器由本章后面将会提及的setitimer和alarm调用设置。缺省行为是终止进程。
fA;Zq yE
(w NT._um
 
#define SIGTERM         15      /* software termination signal from kill */

5~z*yy&{ q.mn*A9@SIGTERM信号被发送给进程,通知该进程是时候终止了,并且在终止之前做一些清理活动。SIGTERM信号是Unix的kill命令发送的缺省信号,同时也是操作系统关闭时向进程发送的缺省信号。缺省行为是终止进程。BSD爱好者乐园H v r+|Z0h3L
BSD爱好者乐园U.bEbD+Hs#z
 
#define SIGURG          16      /* urgent condition on IO channel */
BSD爱好者乐园%~xJWL {;Sr
在进程已打开的套接字上发生某些情况时,SIGURG将被发送给该进程。如果进程不捕获这个信号的话,那么将被丢弃。缺省行为是丢弃这个信号。
b0R)A2{9f_7yOBSD爱好者乐园X!w4f'If S%O
 
#define SIGSTOP         17      /* sendable stop signal not from tty */
BSD爱好者乐园(? W:M-lE/N
本信号不能被捕获或忽略。一旦进程接收到SIGSTOP信号,它会立即停止(stop),直到接收到另一个SIGCONT信号为止。缺省行为是停止进程,直到接收到一个SIGCONT信号为止。BSD爱好者乐园NwDz9xB9u:oW

2We}]-Pf~,Q
 
#define SIGTSTP         18      /* stop signal from tty */

8lH4h+v-ooj"n3h#RSIGSTP与SIGSTOP类似,它们的区别在于SIGSTP信号可以被捕获或忽略。当shell从键盘接收到CTRL-Z的时候就会交付(deliver)这个信号给进程。缺省行为是停止进程,直到接收到一个SIGCONT信号为止。BSD爱好者乐园 BoMV0SX&y$u)L
BSD爱好者乐园%B,v y0_3Ag [m a!g
 
#define SIGCONT         19      /* continue a stopped process */
BSD爱好者乐园[8~d"y@
SIGCONT也是一个有意思的信号。如前所述,当进程停止的时候,这个信号用来告诉进程恢复运行。该信号的有趣的地方在于:它不能被忽略或阻塞,但可以被捕获。这样做很有意义:因为进程大概不愿意忽略或阻塞SIGCONT信号,否则,如果进程接收到SIGSTOP或SIGSTP的时候该怎么办?缺省行为是丢弃该信号。
-GTp_zdjcBSD爱好者乐园*dKsb|F3x
 
#define SIGCHLD         20      /* to parent on child stop or exit */
BSD爱好者乐园,C9C.~6F%Ku
SIGCHLD是由Berkeley Unix引入的,并且比SRV 4 Unix上的实现有更好的接口。(如果信号是一个没有追溯能力的过程(not a retroactive process),那么BSD的SIGCHID信号实现会比较好。在system V Unix的实现中,如果进程要求捕获该信号,操作系统会检查是否存在有任何未完成的子进程(这些子进程是已经退出(exit)的子进程,并且在等待调用wait的父进程收集它们的状态)。如果子进程退出的时候附带有一些终止信息(terminating information),那么信号处理句柄就会被调用。所以,仅仅要求捕获这个信号会导致信号处理句柄被调用(译注:即是上面说的“信号的追溯能力”),而这是却一种相当混乱的状况。)
~9UqC(u_3UsBSD爱好者乐园7VZ.[s-s"o+F?
一旦一个进程的子进程状态发生改变,SIGCHLD信号就会被发送给该进程。就像我在前面章节提到的,父进程虽然可以fork出子进程,但没有必要等待子进程退出。一般来说这是不太好的,因为这样的话,一旦进程退出就可能会变成一个僵尸进程。可是如果父进程捕获SIGCHLD信号的话,它就可以使用wait系列调用中的某一个去收集子进程状态,或者判断发生了什么事情。当发送SIGSTOP,SIGSTP或SIGCONF信号给子进程时,SIGCHLD信号也会被发送给父进程。缺省行为是丢弃该信号。
UxW]a:|7{*[kBSD爱好者乐园7y,Ao)L m }e"_*r
 
#define SIGTTIN         21      /* to readers pgrp upon background tty read */

T$y+J7WL D当一个后台进程尝试进行一个读操作时,SIGTTIN信号被发送给该进程。进程将会阻塞直到接收到SIGCONT信号为止。缺省行为是停止进程,直到接收到SIGCONT信号。BSD爱好者乐园9N&K5d!@4`;mA

f|7^.X[-LDe!u#j
 
#define SIGTTOU         22      /* like TTIN if (tp->t_local&LTOSTOP) */
BSD爱好者乐园f7m'{6D6t+ViI R j
SIGTTOU信号与SIGTTIN很相似,不同之处在于SIGTTOU信号是由于后台进程尝试对一个设置了TOSTOP属性的tty执行写操作时才会产生。然而,如果tty没有设置这个属性,SIGTTOU就不会被发送。缺省行为是停止进程,直到接收到SIGCONT信号。
4V,R3A'~ NXd i s3Uf#FBSD爱好者乐园4_ Kw-O^.Y
 
#define SIGIO           23      /* input/output possible signal */
BSD爱好者乐园k0}Rf1{Q-kx c
如果进程在一个文件描述符上有I/O操作的话,SIGIO信号将被发送给这个进程。进程可以通过fcntl调用来设置。缺省行为是丢弃该信号。
[s']~c ku8]hl
"ZJMf)C(J)m
 
#define SIGXCPU         24      /* exceeded CPU time limit */
BSD爱好者乐园|]M9AW`7L/a4X|9p&SU
如果一旦进程超出了它可以使用的CPU限制(CPU limit),SIGXCPU信号就被发送给它。这个限制可以使用随后讨论的setrlimit设置。缺省行为是终止进程。
.~ X:N%d/m.{(zBSD爱好者乐园 MX~\ TWI!B-bE4S
 
#define SIGXFSZ         25      /* exceeded file size limit */
BSD爱好者乐园!n/y0A9O+q"h$u+A
如果一旦进程超出了它可以使用的文件大小限制,SIGXFSZ信号就被发送给它。稍后我们会继续讨论这个信号。缺省行为是终止进程。BSD爱好者乐园X^s\}*g0bB:^S
BSD爱好者乐园f-YZ0F7g^ Xa
 
#define SIGVTALRM       26      /* virtual time alarm */
BSD爱好者乐园.QO8p:T ^| R'n/[
如果一旦进程超过了它设定的虚拟计时器计数时,SIGVTALRM信号就被发送给它。缺省行为是终止进程。
$G4`8e rx"}R]BSD爱好者乐园qZ:n? E
 
#define SIGPROF         27      /* profiling time alarm */

pra5LXF当设置了计时器时,SIGPROF是另一个将会发送给进程的信号。缺省行为是终止进程。BSD爱好者乐园bF OEL!khsE
BSD爱好者乐园Q5JU2f eBX,^;t
 
#define SIGWINCH        28      /* window size changes */
BSD爱好者乐园@*D8qD2b;d8Jp6Z
当进程调整了终端的行或列时(比如增大你的xterm的尺寸),SIGWINCH信号被发送给该进程。缺省行为是丢弃该信号。
wX8[vY-b;{)Q
+i |6`\!Sm
 
#define SIGUSR1         29      /* user defined signal 1 */BSD爱好者乐园f^7Y+x/\0]
#define SIGUSR2         30      /* user defined signal 2 */
BSD爱好者乐园2UW Xzyc mL
SIGUSR1和SIGUSR2这两个信号被设计为用户指定。它们可以被设定来完成你的任何需要。换句话说,操作系统没有任何行为与这两个信号关联。缺省行为是终止进程。(译注:按原文的意思翻译出来似乎这两句话有点矛盾。)BSD爱好者乐园Qqf&@B o&i4]

9`NU)S G!e!s[size=-1]4.3 系统调用BSD爱好者乐园RB o%h$[-u2~{K K&N,V
那么,你该如何使用信号呢?有时候甚至拿不准是否应该使用信号。例如,当信号被交付的时候,一方面你可以在行为发生之前,分析当前情况,找出信号发生的原因,或者找到这些信号是从哪里发出来的;另一方面,其他一些时候你也可以只是希望简单的退出程序,并且在清除之后创建一个核心转储文件。参见最后部分的简单代码可以获得这些函数的较详细的例子。BSD爱好者乐园V b2M(m5mC u;NZ1A
BSD爱好者乐园9zdoa yI3xw
Kill函数BSD爱好者乐园zx)kwlt"egM,z
kill函数对于那些经常在命令行使用kill命令杀死进程的人来说是再熟悉不过的。基本语法是:
xwZG]5yBSD爱好者乐园-] \ j m.KgID
 
int   kill(pid_t pid, int sig);

DBR b%y@%im8bKill函数将指定的信号发送给进程号为pid的进程。只有当进程符合下面几点情况的时候信号才会被交付:BSD爱好者乐园5`|+yx[A O^K
BSD爱好者乐园8Y})D!{L3s.D$C
QUOTE:
•        发送与接收进程有相同的有效用户ID(UID);
*O4I w*r/D1a2[/c8FD•        发送进程有适当的权限(例如:setuid命令);
g+G}g.l0A-v)B1l•        发送进程有超级用户(root)的UID。
BSD爱好者乐园 K |lvew;S@2d^
注意:SIGCONT信号是一个特例,它可以由当前进程发送给任何一个该进程的派生进程。BSD爱好者乐园Y%]0|TS,p

O6g_8O\4s^*Z使用不同的调用参数使得kill函数的行为差别非常大。这些行为如下所述:BSD爱好者乐园'TZP@,\2Pfk2r
(译注:下面的PID应该指的是上面kill函数原型中的那个pid,我在这里做出说明并保留原文)BSD爱好者乐园t;jsy9H"lR[
BSD爱好者乐园s,WL-S z
QUOTE:
•        如果PID大于0,并且发送进程有适当的权限,那么参数sig指定的信号将被交付。
_/j&p"Iw8PX•        如果PID等于0,那么sig信号将被交付给所有那些与发送进程有相同组ID的进程。(发送进程同样需要满足权限需求。)BSD爱好者乐园4UCd*A [#C q Tm#\
•        如果PID是 -1,那么信号将被发送给所有那些与发送进程有相同有效用户ID的进程(不包含发送进程在内)。然而,如果发送进程的有效用户ID与超级用户(root)的相同,那么信号被交付给除了系统进程(由它们的proc结构中的p_flag域是否是P_SYSTEM来定义)之外的所有进程。在这个特殊的例子中,如果某些进程不能被发送(could not be sent)sig信号,kill函数并不返回一个错误。
*}-Fy YJ3}8hm•        如果sig是0,kill函数只检查错误(例如,无效权限,不存在的进程等)。该用法有时候用来检查一个指定进程是否存在。BSD爱好者乐园B,M#pT(LFN
•        如果成功的话kill函数返回0,否则返回-1。kill调用失败时会在errno全局变量中设置相应的错误值。

s/y_ h3f%ekill的另一个版本是raise函数:
/MFL!w&v1b |BSD爱好者乐园h&g??6T[)h/no
 
int    raise(int sig);

Z-}eM&FdWvraise函数会向当前进程发送sig信号。该函数用处不是很大,因为它只能够给当前进程发送信号。raise函数调用成功时返回0,否则返回-1。调用失败时会在errno全局变量中设置相应的错误值,效果和signal函数的返回类似:(译注:原文只有两个单词“as in:”,我并不知道作者把signal列在这里所要表达的意思,所以我按照我的理解+猜测来翻译的。如果大家有什么建议,或是需要纠正的话,请一定告诉我。)BSD爱好者乐园 PSC8z[W"m,k.v.U
BSD爱好者乐园!eoJ&_zv;u]
 
void (*signal(int sig, void (*func)(int)))(int);

(u ~aVg:cjt1|j4.4 信号处理BSD爱好者乐园Mv0V.G*C5i y
现在我们知道何时会产生信号,也知道如何发送信号,那么我们怎么处理它们呢?BSD爱好者乐园6dH'z7{_.C/N
BSD爱好者乐园#K%\wD$_0\?5cB
signal函数BSD爱好者乐园*xZ? F8k4v#Q
BSD爱好者乐园R.N{!~$g
signal系统函数调用提供了一种最简单的范例。然而,由于C原形声明的缘故使它看起来比实际复杂。signal函数将一个给定的函数和一个特定的信号联系。这里是FreeBSD中的定义(和一个typedef一起):
.r6U,`r+o,evd!^$Q;oBSD爱好者乐园TY.Gtl2VO3dm0O
QUOTE:
typedef void (*sig_t) (int);
}0[2D%K v5o
0Op9omWMsig_t   signal(int sig, sig_t func);
BSD爱好者乐园qp,jT Up*@!dp
第一个参数是目标信号,可以是上面列举的所有信号中的任何一个。func参数是一个指针,指向某个处理该信号的函数。这个处理信号函数带有一个int型参数,并应返回void。signal函数中的func参数也可以设定为下面的一些值:BSD爱好者乐园1o"Fc[?$`-nSh

QO'zM%^$qR
QUOTE:
SIG_IGN: 如果func参数被设置为SIG_IGN,该信号将被忽略。BSD爱好者乐园/@\4^0~y

aO U^^;d)N;a[ASIG_DFL: 如果func参数被设置为SIG_DFL,该信号会按照确定行为处理。

,rer*vYk(|.esigaction函数BSD爱好者乐园S#au'ka}1@!l
BSD爱好者乐园"l:M$K,e2I
sigaction函数是一个比signal更通用的方案。第一个参数是目标信号。下一个名为act的参数(指向)sigaction结构,该结构包含一些用于信号处理的信息。最后一个参数oact是一个指针,指向一个可以存储上一次设置信号处理的信息的地方。
m r0P2r"r/H
K D9tAe#r&t;YH
QUOTE:
int  sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
BSD爱好者乐园d!P0{,@Vx
sigaction结构有下面这些个成员:
s/q$ZiER$cd&RBSD爱好者乐园/y1V ?(A8WwJ `"i
QUOTE:
void     (*sa_handler)(int);
BSD爱好者乐园'V&f GJ5Bg4H&ip
这个结构成员是一个指向函数的指针,该函数带有一个简单的整形参数,并返回(void)。这与signal函数的func参数相同,也可以被设置为SIG_IGN和SIG_DFL,并且与调用signal得到的效果也一样。
"^Kl%P M*E
#Ptx:u4Qg#at6Z6XG
QUOTE:
void     (*sa_sigaction)(int, siginfo_t *, void *);

)NaY}9gX6i该结构成员是一个指向函数的指针,返回(void)并需要三个参数。这些参数依次为:一个整形参数指定信号发送;一个指向siginfo_t结构的指针用来保存关于信号的信息;最后一个也是一个指针,指向信号交付时的特定上下文(context)空间。
t,dt5H1RE]BSD爱好者乐园0LTY3z+jH.w/Wr{ M
QUOTE:
sigset_t sa_mask;
BSD爱好者乐园R9{ @J}t6R2g&K
该结构成员是一个位掩码(bitwise mask),用来指示信号交付时哪些信号会被阻塞。阻塞SIGKILL和SIGSTOP信号的做法会被忽略。接下来,被阻塞的信号将被推迟,直到它们被开启(unblock)。参见sigprocmask获得更多关于全局掩码(global masks)的信息。BSD爱好者乐园/~3g!~`Yn

S0\]0aGA Z)u
QUOTE:
int      sa_flags;
BSD爱好者乐园nR8C,h'iH
该数据成员是一个拥有下面这些标志的位掩码:BSD爱好者乐园X7x.[_)w3W5f2R3FN fz
BSD爱好者乐园!u2k_X2R-h4|
QUOTE:
SA_NOCLDSTOP: 如果SA_NOCLDSTOP位被置位并且目标信号是SIGCHLD,除非子进程退出,而在子进程停止(stop)时父进程将不会收到通知。
/S)cT!VIL
eUK/Pc8c#dSA_NOCLDWAIT: SA_NOCLDWAIT标志会阻止子进程成为僵尸进程。在目标信号是SIGCHLD的时候使用。如果进程设置了这个标志,接着调用某个wait系统调用,进程将被阻塞直到子进程全部终止,最后返回-1(译注:此处在APUE2ed中的解释是返回1),设置errno全局变量为ECHILD。BSD爱好者乐园[@S `6`C f

CoN9nISA_ONSTACK: 一些时候需要在特定的堆栈上进行信号的处理。sigaction系统调用提供了这个方式。如果该位被置位,那么信号将会被交付到指定的堆栈上。
:A_*_Qc9eG3y3Yw
D5F?q8plSA_NODEFER: 如果SA_NODEFER位被置位,那么当前信号正被处理时,系统不会屏蔽该信号以后的交付。BSD爱好者乐园^[Tk)?!r^?7r Q
BSD爱好者乐园5dr/d:na~:e+{
SA_RESETHAND: 如果SA_RESETHAND被置位,一旦信号被交付,信号处理句柄将被置为SIG_DEF。
4@6FK2RK
$c#c3\9hoNXSA_SIGINFO: 被置位时,由结构体sigaction 的成员sa_sigaction指向的函数被使用。注意:使用SIG_IGN或SIG_DFL时不应该设置这个标志。成功调用sigaction之后,返回0或-1,并且将error设置成相关错误值。
BSD爱好者乐园 Q)[]1H TY_"ow
4.5信号掩码(阻塞与开启信号)BSD爱好者乐园'Q'xcX&l
BSD爱好者乐园xY.? M?
进程可以阻塞或设置某个信号。一旦该信号被阻塞,关于它的交付将被推迟,直到进程重新开启它。在这样的情况下是非常有用的:进程进入代码中某个部分,不能被中断但仍希望可以接受、处理可能丢失的信号。可靠交付信号的能力直到4.2BSD引入之后(不久被SVR3采用),操作系统才拥有该能力。BSD爱好者乐园8L!t5G3G`e]
BSD爱好者乐园lF0Wu/XR.fq4[
随着可靠信号的出现,信号的生命和交付(life and delivery)都有所改变。信号可以在之前产生和交付。现在,一旦信号是挂起的(pending),进程可以在接收它之前决定怎么处理。进程可能会去处理它,也可能设置为缺省行为,或者丢弃信号BSD爱好者乐园V ti+Y"?n:TtfS L

P+H]9Xmf注意:如果许多信号都挂起,系统将会首先交付会改变进程状态的信号,例如SIGBUS。BSD爱好者乐园wz~O'Kn.C

!CK&o)`"wu-P(s h GK@sigprocmaskBSD爱好者乐园_HA"P%_O2K [
BSD爱好者乐园J"J&p+w)c$fJ,`
任何进程可以使用sigprocmask函数来阻塞信号。语法如下:BSD爱好者乐园2_G1tLO
BSD爱好者乐园)Y6_6~4W r%@U
QUOTE:
int     sigprocmask(int how, const sigset_t *set, sigset_t *oset);

/J3n7DU5yalsigprocmask函数会修改或检查(modify or examine)当前信号掩码。当set参数不是null的时候,sigprocmask的行为和第一个参数how有关。函数行为和相关意义列举如下:BSD爱好者乐园!E}"yk |&v D

o"UE1zQn7D
QUOTE:
SIG_BLOCK: 在set参数中指定的信号被阻塞,并且添加进阻塞信号列表。BSD爱好者乐园!k&e8u P N5o#F
BSD爱好者乐园N?2A?(Ju.\r}&x
SIG_UNBLOCK: 在set参数中指定的信号会从信号掩码中移除。
J \.R"OeQI5W
:l5HC0E `R]XR'P*nSIG_SETMASK: set参数将完全替代当前信号掩码。如果oset参数不为null,则会被设置为前一个信号掩码。如果set值是null,how参数被忽略并且信号掩码保持不变。所以,为了检查信号掩码,我们可以将传入set null值,oset为非null值来调用sigprocmask函数。一旦掩码得到之后,你可能需要对他进行检查或操作。可以使用下面的过程(routine)。注意当前这些过程是宏的实现。

MI;c9Y;E*I
 
int    sigemptyset(sigset_t *set)

sZ u |,vjZf如果调用这个过程,set参数将被初始化指向一个空信号集。BSD爱好者乐园"`C].c["e%[ z8icBj]
BSD爱好者乐园y9f9beFrI
 
int    sigfillset(sigset_t *set)
BSD爱好者乐园a.VQ}U9_;L)m
如果调用这个过程,set参数将被初始化指向一个包括所有信号的信号集。BSD爱好者乐园,J_L6@ F n
BSD爱好者乐园f{/OB7F9wADP
 
int   sigaddset(sigset_t *set, int signo)

I0N7H!mpC如果调用这个过程,signo指定的信号将被添加进set参数指定的信号集。
ISJ,f1En[7n#k ^
\8p$V2C2Nj6^5l5F d
 
int    sigdelset(sigset_t *set, int signo)

8` sQ:hrLT3]如果调用这个过程,signo指定的信号将从set参数指定的信号集中移除。BSD爱好者乐园.gz-mF8N#}{

'qHz8vHN4QW:j
 
int   sigismember(const sigset_t *set, int signo)
BSD爱好者乐园? qYu_'^@/K
如果调用这个过程,如果由signo指定的信号存在于set参数指定的信号集中时,返回1,否则返回0。BSD爱好者乐园1k7hFF;po

#H"?V:W7x-y/r
 
int     sigpending(sigset_t *set);
BSD爱好者乐园'e([~&pZ'tp
进程可以使用sigpending函数去查出当前那些信号被挂起。sigpending函数会返回一个包含所有挂起信号的掩码。该掩码可以使用上面介绍的过程去检查。sigpending成功时返回0,否则返回-1,并且设置errno为相应错误值。BSD爱好者乐园1Vi[ k,Xn Y rsm
BSD爱好者乐园riNS)tW4A$r[
4.6 自定义行为BSD爱好者乐园'VQ9\3^Y
一些时候程序要求信号处理句柄运行在一个特定的堆栈上。为了实现这个目的,一个备用(alternate)堆栈区间必须用signaltstack函数指出来。这个函数使用的数据结构为signaltstack:BSD爱好者乐园K0H7j:o(_4[

&n8abxr7]%i
 
int    sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);
BSD爱好者乐园A0o?;F/~^%M
它的结构成员解释如下。
AH%cf%GCuJ2G ~-X
U*e${!KD t$iZMZ
 
char    *ss_sp;

.N]We/gU:sx该成员指向一个被用作堆栈的区域。系统中有个MINSIGSTKSZ常量,它定义了进行信号处理时所需的最小内存空间。系统中还有一个SIGSTKSZ常量,它定义了通常情况下处理时所需内存空间。该内存空间需要在调用signaltstack函数之间分配。BSD爱好者乐园"Y Q^!? Xye|
BSD爱好者乐园 Q9?9DO:w!dn
 
size_t  ss_size;
BSD爱好者乐园!N H&~ j}9H\%a
数据成员ss_size指出新堆栈的大小。如果这个值是错误的(inaccurate),当信号处理句柄执行时,它的行为就变得不可预知(你不能明确知道系统怎样处理这个信号)。
X3Kw/UZ
3{_(ywE2\ z&D"K
 
int     ss_flags;
BSD爱好者乐园/^M(MU({#d
根据调用环境(calling circumstances),数据成员ss_flags可以具有少数几个不同的值。首先,当进程希望停用备用堆栈的时候,ss_flags会被设为SS_DISABLE。在这个情况下,ss_sp和ss_size被忽略,备用堆栈被禁止。注意,备用堆栈只能在当前句柄没有处理时禁止。
[-rf)au4g"B LBSD爱好者乐园a(CH},V
如果使用一个non-null值作为oss的实参去调用signaltstack,ss_flags将包含指示当前状态的信息。它们是:BSD爱好者乐园'qE }i;jp
BSD爱好者乐园s X3N%?Qy@
 
SS_DISABLE: 备用堆栈被停用。
Wvs.Dic4R5\+]SS_ONSTACK: 备用堆栈当前正在被使用,并且现在不可以停用。

!E o*V c\'CA^ L如果调用signaltstack 的oss实参不是null,会返回当前状态。调用成功返回0,否则返回-1。如果调用失败,errno也会相应的被设置。因为信号可以在任何一点被交付,所以很难被预测。出于这个原因,4.2BSD的缺省行为是:重新开始被中断的系统调用,重新提供还没有被转送的数据。在大多数时候这个行为是很不错的,并且也是所有BSD系统采用的缺省行为。可是,也有一些罕见的情况,你可能需要将这个特性关掉。你可以使用siginterrupt函数完成需求。使用很简单:
c+EHkO:W6~kBSD爱好者乐园 `%m q']&Ah6I QN
 
int   siginterrupt(int sig, int flag);

d,h6HKoY将sig参数设置为目标信号,并且设置flag为真(在这个情况下是1)。如果flag参数被设为假(在这个情况下是0),那么缺省行为是重新启动系统调用。BSD爱好者乐园X7he+r3F
BSD爱好者乐园&l8nZ}Q

4.7 等待信号

n9Sk"iP,wBSD爱好者乐园H,p)XV ];P@d
sigsuspend函数可以暂时将当前阻塞信号集改变为由sigmask指定的信号集。改变后,sigsuspend会等待,直到一个信号被交付。一旦一个信号被交付后,原先的信号集被恢复。由于sigsuspend调用在信号交付之后总是被终止,它的返回值总是-1,errno总是EINTR。下面是它的语法:
r;{#JUuX
Tb-P.hbol\
 
int     sigsuspend(const sigset_t *sigmask);

{BaT7G'sNsigwait函数用set参数指定的信号集作为信号掩码。它会检查包含在这个特定集合内的是否有任何挂起信号,如果有的话,它将清除这个挂起的信号,并在sig参数中返回这个被清除信号的数值。如果没有信号挂起,sigwait将一直等待直到指定信号集合中的任何一个信号产生。下面是它的语法:
c`{_5qkHBSD爱好者乐园&JxVW m7XS,q
 
int    sigwait(const sigset_t *set, int *sig);

$tP-jaI当信号被交付给进程(该进程安装了相应信号的处理句柄)时,进程将会切换到信号处理句柄中执行。例如,假设你的程序监听一个由配置文件设定的端口。你的进程安装了一个捕获SIGHUP信号的处理句柄来重新读取配置文件。一旦SIGHUP信号被交付给你的程序,进程将会执行信号处理句柄来重新读取配置文件。这里存在一个问题,你没有办法知道在进程执行过程中信号被交付的确切地点。你虽然可以使用一些下面列出的函数来将范围缩小,但是如果碰到像打开套接字,打开链接,或者其它那些首先需要清理后才能在新端口上监听 这些情况的时候呢?你怎样确定清理活动在那里开始,什么时候开始?如果你的程序正在等到输入,并且没有数据被传进来的时候,系统调用将被重启(system call will be restarted),所以从SIGHUP的返回将会继续等待。BSD爱好者乐园Yf*LP+ztW%Q

r-pV ?Z!T dr这是使用setjmp和longjmp函数的一些情况,这些函数提供非本地分支(non-local branching)。为了使用这些setjmp函数,需要提供一个evn参数,如下:
+YJ,F(y x A1ko"f%u b
MgI!W*O
 
jmp_buff  env;
.m.Q J YThh/}6iw  int    sigsetjmp(sigjmp_buf env, int savemask);BSD爱好者乐园we!lO6l2Gs p
  void   siglongjmp(sigjmp_buf env, int val);
:^m%sy-J|+nSd  int    setjmp(jmp_buf env);BSD爱好者乐园_8[ [N$X
  void   longjmp(jmp_buf env, int val);BSD爱好者乐园Dk c[ ix@"`T
  int     _setjmp(jmp_buf env);BSD爱好者乐园t5T2eT8Xiz}
  void    _longjmp(jmp_buf env, int val);BSD爱好者乐园,}SYv*K8D/V [
  void    longjmperror(void);

Jv rN9\s J'b首先,调用setjmp的返回为0,当前的环境将被保存在env中。接着,你可以在信号处理句柄内部调用对应的longjmp。一旦调用了longjmp,它将把执行环境恢复为env中保存的环境,并返回到最初setjmp被调用时的环境中。最初的setjmp调用返回那个传递给longjmp的va参数的值。
7w5@xZE d-V
n$Pi w p9te L_7\关于setjmp和longjmp函数一些说明:首先,这两个是不能混杂使用。也就是说,调用setjmp时保存的env变量不能传递给_longjmp调用。另外,调用setjmp的函数返回后,接下来调用longjmp将会发生错误。
frk$n"W J ^mThe different calls have specific actions that they take. These actions are listed below:BSD爱好者乐园foA8@ r
不同的调用有特定的行为。这些行为在下面列出来:
)Y"X1st6@.o8`2h
$ua \"qK5l%ZZh
 
jmp和longjmp: 他们会保存(恢复)信号掩码,寄存器组和堆栈。
BSD爱好者乐园xNg`E(v$^3N|
BSD爱好者乐园cz;];~@&B
 
_setjmp和_longjmp: 他们只保存(恢复)寄存器组和堆栈。
BSD爱好者乐园k#ct)X+^ ~
BSD爱好者乐园4k t%Do'ku$P
 
sigsetjmp和siglongjmp: 只要savemask参数不是0,他们就保存(恢复)寄存器组,堆栈和信号掩码。

2F/K^V| R由于一些原因,如果env参数保存的东西被破坏,或者调用setjmp的函数返回了,longjmp函数将会调用longjmperror函数。如果longjmperror也返回了,程序将被异常终止。你可以使用与longjmperror有相同原形的函数来自定义longjmperror函数。缺省的longjmperror会在标准错误上输出”longjmp botch”,然后返回。BSD爱好者乐园"py oT4c j
4.8 AlarmsBSD爱好者乐园*\'j$\+b&c a8{$k}m9z

?6O:e"\ h*x3s i9b!f
 
unsigned int   alarm(unsigned int seconds);

M4\e"z!t7r0walarm函数基本上是一个简单的闹钟时钟(alarm clock),同时也是一个很有用的函数。它允许进程在经过指定秒数之后收到一个通知。一旦闹钟时间到,进程将收到一个SIGALRM信号。任何随后的alarm调用都会覆盖原先的调用设定。alarm不像sleep函数,它不会被阻塞。BSD爱好者乐园 fDuI?F3]

"fz6K/R]j/R)m它有一些返回值需要值得你注意:首先,如果进程没有设定定时器,那么返回值是0。其次,如果有一个定时器被设定但还没有超时的话,那么会返回前一个调用到现在还有的剩余时间。BSD爱好者乐园J_p)I&ErtM8r$Hd

?$U;_ S[M-_x现在可以设定的最大时间是100,000,000秒 --- 已经是相当长的时间了。BSD爱好者乐园6QZh#t9PA%h7j YxN|

vl(n`?#o6P WH"e
 
int getitimer(int which, struct itimerval *value);

/~@+g'XncDgetitimer函数会检索由第一个参数(which参数)描述的itimerval结构。第一个参数可选的选项将在下面说明:BSD爱好者乐园l z;ip$r k3OL'U6j~

2I*ZBEG2hR"E S
 
int  setitimer(int which, const struct itimerval *value,  struct itimerval *ovalue);
BSD爱好者乐园%h]wK&A HQ3Vd
setitmer比先前的alarm调用提供了更稳定的接口。在BSD系统上,每个进程可以提供三种不同时间间隔的定时器。他们在下面讲述:
)qqdNVBSD爱好者乐园&w ? Z!AI/dh{+\]
 
#define ITIMER_REAL      0

\(be/C_G~实时时钟实时的递减而不管进程在CPU上的实际花销时间(换句话说,它追踪自然时间natural time)。这允许进程设置一个基于自然实时时间(based on  atural real time)的定时器。当实时定时器超时的时候,进程会收到SIGALRM信号。
BrM7lDy$]s j s
4z8S:GdN
 
#define ITIMER_VIRTUAL   1

s])M#S-C1]B虚拟定时器仅只递减进程在CPU上的执行时间,允许进程设定一个基于CPU使用率的定时器。当虚拟定时器超时的时候,进程收到SIGVTALRM信号
$eYt3|d1@q`
{"t wc H~+_3?F7H
 
#define ITIMER_PROF      2
BSD爱好者乐园Xo+Q&\/j4?%l;ub4X/d?l.^
Profile定时器递减在CPU上的执行时间和代表进程执行的系统调用的时间。这对于那些要求静态剖析的解释程序是很有帮助的。当profile定时器超时的时候,进程收到SIGPROF信号。然而并不像实时和虚拟定时器那样,SIGPROF可以在系统调用的时候被发送;进程应该准备好重新执行被中断的系统调用。BSD爱好者乐园 u0K;VTA4?
BSD爱好者乐园&Z~:|khe|$Zp
本章将焦点放在了信号库上。这些信号及他们的使用方法对于系统编程是很重要的。信号允许系统管理员通知应用程序重新读取配置文件,从而使程序更稳定。其它重要的信号处理挂起在打开的文件描述符上的I/O操作。下一章讲述怎样利用这些I/O相关的信号。因为使用范围的限制,BSD方面文章更新速度不快,站长会坚持每天更新博客,欢迎访问!
[版权声明]BSD爱好者乐园站内文章,如来源不是互联网,则均系原创或翻译之作,可随意转载,或以此为基础进行演译,但务必以链接形式注明原始出处和作者信息,否则属于侵权行为。另对本站转载他处文章,俱有说明,如有侵权请联系本人,本人将会在第一时间删除侵权文章。
TAG: freebsd FreeBSD 系统编程
 

评分:0

我来说两句

seccode