网络推荐



本广告位招租!

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

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

翻译:gvim@chinaunix/bsdBSD爱好者乐园:sW[5{ y-J$]I#^
一些说明:BSD爱好者乐园jh T1\|0U
1 很高兴有这个机会参与到这项活动中来。
M*Af;u)YPq+_2 由于英语语言的习惯,文中有不少复杂长句,按照字面翻译出来并不适合中国人的阅读习惯。我在保留文章原意的基础上作了一些语言组织上的调整,将大部分长句子组织成中文擅长的短句型,所以你会发现翻译品一部分内容不是按照原文字面翻译的。BSD爱好者乐园9_2G~F3k?
3 考虑到该书的入门引导作用,在原文中一些比较少见的词后面我添加了少许译注,希望减少入门兄弟的负担。BSD爱好者乐园4\q2q-n4ItB
4 由于2中的语言调整和3中的译注,是我的个人行为,可能会有我对 原文理解模糊,概念不清,或是语言组织不顺畅的地方,还请大家一定多多指出来。谢谢。
6NjG R(l1D5C o/R-k个人声明:BSD爱好者乐园 J ceXzZ Ew
本翻译品受控于chinaunix BSD翻译小组。如需单独转载,请保留“翻译:gvim@chinaunix/bsd”和以上几点说明。

)QkR T0c#BL4.1 高级进程控制和信号
*r&aqu5n信号:4 a:一种对象,用于传输或负载人类声音之外的信息。
#p v#H^IvJ,yBSD爱好者乐园 ? oU+k,k4m c,\9| {U
到目前为止,我们已经讨论了进程的创建和其他系统调用。现在是讨论下面这些问题的时候了:你要在多个进程间通讯以获得更好的进程控制粒度,或者要其他程序或操作者用信号通知你的程序。例如,你可能希望你的程序重新读取它的配置文件。或者,你的数据库程序需要在退出之前将事务从主存写入后备存储器,然后再退出。这两个例子可能只是使用信号的很小一部分。虽然已经有套接字,先入先出队列,管道,信号量等多种方式来完成类似的任务,但是我们将把讨论的焦点放在信号和其它进程控制机制上。在现实中,信号和进程控制机制可以提供大部分你所需要的特性和功能。
TFJ/O}*sq aBSD爱好者乐园$Z/X`5y~
4.2 信号
+\1q}f:Vm[d2\信号与硬件中断很相似。当设备需要中断服务的时候它可以产生一个硬件中断来通知CPU。与硬件中断类似的,当进程需要将一些事件通知给其他进程的时候可以使用信号来完成。BSD爱好者乐园 z Sc'b H
BSD爱好者乐园JoEE]2d-E1X
大多数Unix系统管理员会比较熟悉SIGHUP信号。当你通过kill命令向后台服务进程发出SIGHUP信号后,大多数进程要么重新读入他们的配置文件要么重新启动。这些信号之中,一些与硬件有直接关系,如SIGFPE(浮点异常),SIGILL(非法指令);其它则是与软件相关,如SIGSYS(未实现的系统调用被调用)。
nYG!m#BF
G$f?bW一旦进程接收到信号之后,该信号的行为与信号本身和进程对它的使用目的两个因素有关。一些信号可以被阻塞,忽略,或者捕获,而另外一些则不可以。如果进程需要捕获一个信号并履行一些相关操作,你可以为进程设定这个特定信号的信号处理句柄。处理句柄仅仅是一个函数,在这个信号被进程接收之后调用。或者更确切的说,处理句柄是一个函数调用,你可以对它进行指派(specify)。BSD爱好者乐园ki Z g4aa

A~ c;_S0\ |当信号没有指定处理句柄时,将会执行操作系统默认的行为。这些缺省行为可以是从终止进程到完全核心转储等不同的操作。注意,有两个信号不能被捕获或忽略:SIGSTOP和SIGKILL,下面会解释。
M)u1F)sgk!KJ|BSD爱好者乐园a nDd5\m$PG+I
在BSD系统中定义的有许多信号;我们讨论在/usr/include/sys/signals.h(译注:在我的FB5.2.1中是signal.h)文件中定义的标准信号。注意, NetBSD系统中定义的信号数量稍微多一点,并且我们没有将它们的讨论放在这里。所以如果需要使用某个下面没有涉及到的信号的时候,请查阅你的系统的头文件
h2lz],}BSD爱好者乐园Q$Bb^V4q5W[3KT
 
#define SIGHUP          1       /* hangup */
BSD爱好者乐园$vx3V i&K
SIGHUP是Unix系统管理员很常用的一个信号。许多后台服务进程在接受到该信号后将会重新读取它们的配置文件。然而,该信号的实际功能是通知进程它的控制终端被断开。缺省行为是终止进程。BSD爱好者乐园s+Kb6D\
BSD爱好者乐园.B7E t;Hi$E
 
#define SIGINT          2       /* interrupt */
BSD爱好者乐园Qo+^-qRq(v-ll
对于Unix使用者来说,SIGINT是另外一个常用的信号。许多shell的CTRL-C组合使得这个信号被大家所熟知。该信号的正式名字是中断信号。缺省行为是终止进程。BSD爱好者乐园%L._ `o7[
BSD爱好者乐园1u?4G5S(h T^
 
#define SIGQUIT         3       /* quit */

2L/?9?S wJ-}.gSSIGQUIT信号被用于接收shell的CTRL-/组合。另外,它还用于告知进程退出。这是一个常用信号,用来通知应用程序从容的(译注:即在结束前执行一些退出动作)关闭。缺省行为是终止进程,并且创建一个核心转储。
^Q"t(y'v z&x5hBSD爱好者乐园Z*t3v ^ ldA6F
 
#define SIGILL          4       /* illegal instr. (not reset when caught) */
BSD爱好者乐园sfl]E(YKQ
如果正在执行的进程中包含非法指令,操作系统将向该进程发送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里面的东西,不管怎样,还真不知道,确切含义还请知道的兄弟斧正。)缺省行为是终止进程,并且创建一个核心转储。
Pr?H A7X.X+q
j+S%x2g/|y0E$]~7@$T
 
#define SIGTRAP         5       /* trace trap (not reset when caught) */

v9u$F:gPa+wSIGTRAP这个信号是由POSIX标准定义的,用于调试目的。当被调试进程接收到该信号时,就意味着它到达了某一个调试断点。一旦这个信号被交付,被调试的进程就会停止,并且它的父进程将接到通知。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园5|+S"O y`

3g G6r?|-tk/D
 
#define SIGABRT         6       /* abort() */
BSD爱好者乐园d:Z8v P~*k~"u
SIGABRT提供了一种在异常终止(abort)一个进程的同时创建一个核心转储的方法。然而如果该信号被捕获,并且信号处理句柄没有返回,那么进程不会终止。缺省行为是终止进程,并且创建一个核心转储。
hM`#L3oT E@B(_&aBSD爱好者乐园1M4p M3M1pE
 
#define SIGFPE          8       /* floating point exception */
BSD爱好者乐园? y C `"w(a
当进程发生一个浮点错误时,SIGFPE信号被发送给该进程。对于那些处理复杂数学运算的程序,一般会建议你捕获该信号。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园 O8^ Cy'd Y6UXF ya
BSD爱好者乐园C7Q3U#t? bC D
 
#define SIGKILL         9       /* kill (cannot be caught or ignored) */
BSD爱好者乐园+|w9Hlu q#JPE3j
SIGKILL是这些信号中最难对付的一个。正如你在它旁边的注释中看到的那样,这个信号不能被捕获或忽略。一旦该信号被交付给一个进程,那么这个进程就会终止。然而,会有一些极少数情况SIGKILL不会终止进程。这些罕见的情形在处理一个“非中断操作”(比如磁盘I/O)的时候发生。虽然这样的情形极少发生,然而一旦发生的话,会造成进程死锁。唯一结束进程的办法就只有重新启动了。缺省行为是终止进程。BSD爱好者乐园1YoE)eI DG+^h
BSD爱好者乐园QHK l8^z!m
 
#define SIGBUS          10      /* bus error */
BSD爱好者乐园+s6_ RI9h#| {
如同它的名字暗示的那样,CPU检测到数据总线上的错误时将产生SIGBUS信号。当程序尝试去访问一个没有正确对齐的内存地址时就会产生该信号。缺省行为是终止进程,并且创建一个核心转储。
;K VD4V8u%P ?
/e,D_+oOg
 
#define SIGSEGV         11      /* segmentation violation */
BSD爱好者乐园X!]*Qf6ApNN
SIGSEGV是另一个C/C++程序员很熟悉的信号。当程序没有权利访问一个受保护的内存地址时,或者访问无效的虚拟内存地址(脏指针,dirty pointers,译注:由于没有和后备存储器中内容进行同步而造成。关于野指针,可以参见http://en.wikipedia.org/wiki/Wild_pointer 的解释。)时,会产生这个信号。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园Un(I5Z^8m
BSD爱好者乐园5T4C@d?*JBK
 
#define SIGSYS          12      /* non-existent system call invoked */
BSD爱好者乐园jaX1ou!H7]9L
SIGSYS信号会在进程执行一个不存在的系统调用时被交付。操作系统会交付该信号,并且进程会被终止。缺省行为是终止进程,并且创建一个核心转储。BSD爱好者乐园xl*yN,e5x3{

9uZ0mw!?0~ _+V
 
#define SIGPIPE         13      /* write on a pipe with no one to read it */
BSD爱好者乐园 l0fp Y$J^`
管道的作用就像电话一样,允许进程之间的通信。如果进程尝试对管道执行写操作,然而管道的另一边却没有回应者时,操作系统会将SIGPIPE信号交付给这个讨厌的进程(这里就是那个打算写入的进程)。缺省行为是终止进程。
*Q bD ?"J i
 
#define SIGALRM         14      /* alarm clock */

5X&\4gL {Es)k P在进程的计时器到期的时候,SIGALRM信号会被交付(delivered)给进程。这些计时器由本章后面将会提及的setitimer和alarm调用设置。缺省行为是终止进程。BSD爱好者乐园$XF2s'[3l-o

h#Oy'Gf!Tg ` cR%s
 
#define SIGTERM         15      /* software termination signal from kill */
BSD爱好者乐园C G$~3^Ee ^2H M
SIGTERM信号被发送给进程,通知该进程是时候终止了,并且在终止之前做一些清理活动。SIGTERM信号是Unix的kill命令发送的缺省信号,同时也是操作系统关闭时向进程发送的缺省信号。缺省行为是终止进程。BSD爱好者乐园\ @o(p^1a2FUU
BSD爱好者乐园'S1\Z@.| ]4M
 
#define SIGURG          16      /* urgent condition on IO channel */
BSD爱好者乐园'G3T(Q0p.W5T
在进程已打开的套接字上发生某些情况时,SIGURG将被发送给该进程。如果进程不捕获这个信号的话,那么将被丢弃。缺省行为是丢弃这个信号。BSD爱好者乐园/P&eb.a}'L,?
BSD爱好者乐园P7@+K4F A n$O"Et8a5^
 
#define SIGSTOP         17      /* sendable stop signal not from tty */

i1R} e4GPq本信号不能被捕获或忽略。一旦进程接收到SIGSTOP信号,它会立即停止(stop),直到接收到另一个SIGCONT信号为止。缺省行为是停止进程,直到接收到一个SIGCONT信号为止。BSD爱好者乐园cwY @K$rV
BSD爱好者乐园Vw.wJ2G%Gm f
 
#define SIGTSTP         18      /* stop signal from tty */

s'g2fd?5z$bYj||SIGSTP与SIGSTOP类似,它们的区别在于SIGSTP信号可以被捕获或忽略。当shell从键盘接收到CTRL-Z的时候就会交付(deliver)这个信号给进程。缺省行为是停止进程,直到接收到一个SIGCONT信号为止。BSD爱好者乐园8Mq$` C$H
BSD爱好者乐园.tXTfE
 
#define SIGCONT         19      /* continue a stopped process */

&x `3Y%Hkg7P8wSIGCONT也是一个有意思的信号。如前所述,当进程停止的时候,这个信号用来告诉进程恢复运行。该信号的有趣的地方在于:它不能被忽略或阻塞,但可以被捕获。这样做很有意义:因为进程大概不愿意忽略或阻塞SIGCONT信号,否则,如果进程接收到SIGSTOP或SIGSTP的时候该怎么办?缺省行为是丢弃该信号。BSD爱好者乐园 rf Gp4^*U#i3g)G4P)^
BSD爱好者乐园.x)e!I7ty
 
#define SIGCHLD         20      /* to parent on child stop or exit */

$cJ/B)[ ZLQ0JSIGCHLD是由Berkeley Unix引入的,并且比SRV 4 Unix上的实现有更好的接口。(如果信号是一个没有追溯能力的过程(not a retroactive process),那么BSD的SIGCHID信号实现会比较好。在system V Unix的实现中,如果进程要求捕获该信号,操作系统会检查是否存在有任何未完成的子进程(这些子进程是已经退出(exit)的子进程,并且在等待调用wait的父进程收集它们的状态)。如果子进程退出的时候附带有一些终止信息(terminating information),那么信号处理句柄就会被调用。所以,仅仅要求捕获这个信号会导致信号处理句柄被调用(译注:即是上面说的“信号的追溯能力”),而这是却一种相当混乱的状况。)BSD爱好者乐园I\#kUt {

}h"O+jn4dU7lLV&Q一旦一个进程的子进程状态发生改变,SIGCHLD信号就会被发送给该进程。就像我在前面章节提到的,父进程虽然可以fork出子进程,但没有必要等待子进程退出。一般来说这是不太好的,因为这样的话,一旦进程退出就可能会变成一个僵尸进程。可是如果父进程捕获SIGCHLD信号的话,它就可以使用wait系列调用中的某一个去收集子进程状态,或者判断发生了什么事情。当发送SIGSTOP,SIGSTP或SIGCONF信号给子进程时,SIGCHLD信号也会被发送给父进程。缺省行为是丢弃该信号。
mtn.F@nBSD爱好者乐园"O8j|U Y1h
 
#define SIGTTIN         21      /* to readers pgrp upon background tty read */
BSD爱好者乐园oRc.u)I#WP4T8M
当一个后台进程尝试进行一个读操作时,SIGTTIN信号被发送给该进程。进程将会阻塞直到接收到SIGCONT信号为止。缺省行为是停止进程,直到接收到SIGCONT信号。BSD爱好者乐园F\:L#e9Ir8u$W
BSD爱好者乐园d'YzRaS
 
#define SIGTTOU         22      /* like TTIN if (tp->t_local&LTOSTOP) */
BSD爱好者乐园O[-? ? {
SIGTTOU信号与SIGTTIN很相似,不同之处在于SIGTTOU信号是由于后台进程尝试对一个设置了TOSTOP属性的tty执行写操作时才会产生。然而,如果tty没有设置这个属性,SIGTTOU就不会被发送。缺省行为是停止进程,直到接收到SIGCONT信号。BSD爱好者乐园+L/VJp[,xI
BSD爱好者乐园*m&N(Y,Ip
 
#define SIGIO           23      /* input/output possible signal */

`aT#SW F#@如果进程在一个文件描述符上有I/O操作的话,SIGIO信号将被发送给这个进程。进程可以通过fcntl调用来设置。缺省行为是丢弃该信号。BSD爱好者乐园Q:fc!m,qA/J
BSD爱好者乐园)Q(bxY#n]'X4\
 
#define SIGXCPU         24      /* exceeded CPU time limit */
BSD爱好者乐园8{8?7q5eQ_#J.A r
如果一旦进程超出了它可以使用的CPU限制(CPU limit),SIGXCPU信号就被发送给它。这个限制可以使用随后讨论的setrlimit设置。缺省行为是终止进程。
+E!DB+BA _0s)Q(tsEBSD爱好者乐园'd0G;AWtNz`
 
#define SIGXFSZ         25      /* exceeded file size limit */

zy5tiB\/u3j;Y5{9J如果一旦进程超出了它可以使用的文件大小限制,SIGXFSZ信号就被发送给它。稍后我们会继续讨论这个信号。缺省行为是终止进程。
[z'Kj Lh
ti_W2usC"|0R{
 
#define SIGVTALRM       26      /* virtual time alarm */
BSD爱好者乐园9gsw k/f%v#G
如果一旦进程超过了它设定的虚拟计时器计数时,SIGVTALRM信号就被发送给它。缺省行为是终止进程。BSD爱好者乐园)t2e8UFe,b?M.]"fd

M5}c;Z.]9j {I
 
#define SIGPROF         27      /* profiling time alarm */
BSD爱好者乐园)M Ygn'T8S4m4k!S
当设置了计时器时,SIGPROF是另一个将会发送给进程的信号。缺省行为是终止进程。BSD爱好者乐园)w;v/ND3\w

fAt#Dq1G `?W*ak q
 
#define SIGWINCH        28      /* window size changes */

A+}Gw/w$cx当进程调整了终端的行或列时(比如增大你的xterm的尺寸),SIGWINCH信号被发送给该进程。缺省行为是丢弃该信号。
C%w7NP)LpmH&`(TBSD爱好者乐园5GL [oTB
 
#define SIGUSR1         29      /* user defined signal 1 */BSD爱好者乐园9}h6k"R s
#define SIGUSR2         30      /* user defined signal 2 */
BSD爱好者乐园[ }*cAVET9t
SIGUSR1和SIGUSR2这两个信号被设计为用户指定。它们可以被设定来完成你的任何需要。换句话说,操作系统没有任何行为与这两个信号关联。缺省行为是终止进程。(译注:按原文的意思翻译出来似乎这两句话有点矛盾。)
O]|,?,Q5r q}BSD爱好者乐园)C'L1YrbBuZ1f
[size=-1]4.3 系统调用BSD爱好者乐园:C_B7Uk&F
那么,你该如何使用信号呢?有时候甚至拿不准是否应该使用信号。例如,当信号被交付的时候,一方面你可以在行为发生之前,分析当前情况,找出信号发生的原因,或者找到这些信号是从哪里发出来的;另一方面,其他一些时候你也可以只是希望简单的退出程序,并且在清除之后创建一个核心转储文件。参见最后部分的简单代码可以获得这些函数的较详细的例子。
nK|y"H5IDnBSD爱好者乐园.n9N:^"b5dm
Kill函数
1N%K8q mh$m!rzkill函数对于那些经常在命令行使用kill命令杀死进程的人来说是再熟悉不过的。基本语法是:
)qA+C w/g$[BSD爱好者乐园]L,?H6CL
 
int   kill(pid_t pid, int sig);

9p8^,Sd%H2SW9OKill函数将指定的信号发送给进程号为pid的进程。只有当进程符合下面几点情况的时候信号才会被交付:BSD爱好者乐园$B}j ](avmH

^Xx cqjJ'k5w
QUOTE:
•        发送与接收进程有相同的有效用户ID(UID);BSD爱好者乐园8ILf}.N)J&UG cX R
•        发送进程有适当的权限(例如:setuid命令);BSD爱好者乐园] wHf%v&C,U'I0c(P
•        发送进程有超级用户(root)的UID。

yOA&E0Ro%_U注意:SIGCONT信号是一个特例,它可以由当前进程发送给任何一个该进程的派生进程。BSD爱好者乐园[XX {X1j'_6@
BSD爱好者乐园;x fv$S9h@:db
使用不同的调用参数使得kill函数的行为差别非常大。这些行为如下所述:BSD爱好者乐园 GBYyB.\i
(译注:下面的PID应该指的是上面kill函数原型中的那个pid,我在这里做出说明并保留原文)
R;d[P'{[
Z\9?4_7q[+LZ&Z
QUOTE:
•        如果PID大于0,并且发送进程有适当的权限,那么参数sig指定的信号将被交付。BSD爱好者乐园+V/NfR3g P$[;e
•        如果PID等于0,那么sig信号将被交付给所有那些与发送进程有相同组ID的进程。(发送进程同样需要满足权限需求。)BSD爱好者乐园 J9X Mfy9Mt+{7{
•        如果PID是 -1,那么信号将被发送给所有那些与发送进程有相同有效用户ID的进程(不包含发送进程在内)。然而,如果发送进程的有效用户ID与超级用户(root)的相同,那么信号被交付给除了系统进程(由它们的proc结构中的p_flag域是否是P_SYSTEM来定义)之外的所有进程。在这个特殊的例子中,如果某些进程不能被发送(could not be sent)sig信号,kill函数并不返回一个错误。BSD爱好者乐园FH1e*M+CnG j ga
•        如果sig是0,kill函数只检查错误(例如,无效权限,不存在的进程等)。该用法有时候用来检查一个指定进程是否存在。
8l*A,E~(Qr\•        如果成功的话kill函数返回0,否则返回-1。kill调用失败时会在errno全局变量中设置相应的错误值。
BSD爱好者乐园+z2H!aji+d
kill的另一个版本是raise函数:BSD爱好者乐园 Bb6A$_ } Rf

0v2GG"\:B3`
 
int    raise(int sig);
BSD爱好者乐园H i3R al+f
raise函数会向当前进程发送sig信号。该函数用处不是很大,因为它只能够给当前进程发送信号。raise函数调用成功时返回0,否则返回-1。调用失败时会在errno全局变量中设置相应的错误值,效果和signal函数的返回类似:(译注:原文只有两个单词“as in:”,我并不知道作者把signal列在这里所要表达的意思,所以我按照我的理解+猜测来翻译的。如果大家有什么建议,或是需要纠正的话,请一定告诉我。)
D@'TV0wi-f
:Ha7^\B,O#@]
 
void (*signal(int sig, void (*func)(int)))(int);
BSD爱好者乐园Zd5z2bjf
4.4 信号处理
'W:^ W'nS3`现在我们知道何时会产生信号,也知道如何发送信号,那么我们怎么处理它们呢?
:S5H3lel.Y R5} |z
H6o y8^AD5dr^signal函数
,NU-bfnf o
:B(Ivt#d;I{signal系统函数调用提供了一种最简单的范例。然而,由于C原形声明的缘故使它看起来比实际复杂。signal函数将一个给定的函数和一个特定的信号联系。这里是FreeBSD中的定义(和一个typedef一起):
$G;H1E H*ANBSD爱好者乐园!`HbCR
QUOTE:
typedef void (*sig_t) (int);BSD爱好者乐园3\vw%j kL!m

Bk2l g @sig_t   signal(int sig, sig_t func);
BSD爱好者乐园?K6}/^Mb'T1Ut/h
第一个参数是目标信号,可以是上面列举的所有信号中的任何一个。func参数是一个指针,指向某个处理该信号的函数。这个处理信号函数带有一个int型参数,并应返回void。signal函数中的func参数也可以设定为下面的一些值:BSD爱好者乐园O\:jH(z_Z

X6pL2||+Kb
QUOTE:
SIG_IGN: 如果func参数被设置为SIG_IGN,该信号将被忽略。BSD爱好者乐园Bd4D6t!QO)h `b"?
BSD爱好者乐园6h)@5l9V ]O4B%cV
SIG_DFL: 如果func参数被设置为SIG_DFL,该信号会按照确定行为处理。

YcO]/{I*jpsigaction函数BSD爱好者乐园,A*^~k$Xo2\

:BDsZ5Msigaction函数是一个比signal更通用的方案。第一个参数是目标信号。下一个名为act的参数(指向)sigaction结构,该结构包含一些用于信号处理的信息。最后一个参数oact是一个指针,指向一个可以存储上一次设置信号处理的信息的地方。
q*X6OAIi,@*IbhBSD爱好者乐园v%a.v"]8mkG
QUOTE:
int  sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
BSD爱好者乐园!f&Xk7B:vi)X
sigaction结构有下面这些个成员:BSD爱好者乐园|YM0ttLc
BSD爱好者乐园 IV)nBx)Z]
QUOTE:
void     (*sa_handler)(int);
BSD爱好者乐园7L^ms8B%_2N{
这个结构成员是一个指向函数的指针,该函数带有一个简单的整形参数,并返回(void)。这与signal函数的func参数相同,也可以被设置为SIG_IGN和SIG_DFL,并且与调用signal得到的效果也一样。BSD爱好者乐园5H4f6RXlLV
BSD爱好者乐园6AB8@ q9Myp
QUOTE:
void     (*sa_sigaction)(int, siginfo_t *, void *);

2ti3[2Rt qE3v{t该结构成员是一个指向函数的指针,返回(void)并需要三个参数。这些参数依次为:一个整形参数指定信号发送;一个指向siginfo_t结构的指针用来保存关于信号的信息;最后一个也是一个指针,指向信号交付时的特定上下文(context)空间。
5~5q[!f jzBSD爱好者乐园Rv0dGA6cs;j+~
QUOTE:
sigset_t sa_mask;
BSD爱好者乐园 Q8~*y aC4Z2}2P_
该结构成员是一个位掩码(bitwise mask),用来指示信号交付时哪些信号会被阻塞。阻塞SIGKILL和SIGSTOP信号的做法会被忽略。接下来,被阻塞的信号将被推迟,直到它们被开启(unblock)。参见sigprocmask获得更多关于全局掩码(global masks)的信息。BSD爱好者乐园.w$gmlv9P&n:d

!us!['a3n RzC/x
QUOTE:
int      sa_flags;
BSD爱好者乐园q&z I c2S#C9D@(R
该数据成员是一个拥有下面这些标志的位掩码:BSD爱好者乐园|.k4v^v
BSD爱好者乐园M"]"^UNvj
QUOTE:
SA_NOCLDSTOP: 如果SA_NOCLDSTOP位被置位并且目标信号是SIGCHLD,除非子进程退出,而在子进程停止(stop)时父进程将不会收到通知。
8V9y4M(h5o7a/zBSD爱好者乐园gYW6G-{i(m'b!B5[K
SA_NOCLDWAIT: SA_NOCLDWAIT标志会阻止子进程成为僵尸进程。在目标信号是SIGCHLD的时候使用。如果进程设置了这个标志,接着调用某个wait系统调用,进程将被阻塞直到子进程全部终止,最后返回-1(译注:此处在APUE2ed中的解释是返回1),设置errno全局变量为ECHILD。BSD爱好者乐园x9g!Hyy0L

@ S!~s*iSA_ONSTACK: 一些时候需要在特定的堆栈上进行信号的处理。sigaction系统调用提供了这个方式。如果该位被置位,那么信号将会被交付到指定的堆栈上。BSD爱好者乐园`4|[5v9C'K+CLE

%j~ @h2[SA_NODEFER: 如果SA_NODEFER位被置位,那么当前信号正被处理时,系统不会屏蔽该信号以后的交付。BSD爱好者乐园z%L:N!wM Q

,P$jr yloSA_RESETHAND: 如果SA_RESETHAND被置位,一旦信号被交付,信号处理句柄将被置为SIG_DEF。
f/o}V,YJ;\ JBSD爱好者乐园~ I0L!H G
SA_SIGINFO: 被置位时,由结构体sigaction 的成员sa_sigaction指向的函数被使用。注意:使用SIG_IGN或SIG_DFL时不应该设置这个标志。成功调用sigaction之后,返回0或-1,并且将error设置成相关错误值。

;Jr\sc5^4.5信号掩码(阻塞与开启信号)
&tC.W[;G:j'g v(Q
z@'_1h"F8q进程可以阻塞或设置某个信号。一旦该信号被阻塞,关于它的交付将被推迟,直到进程重新开启它。在这样的情况下是非常有用的:进程进入代码中某个部分,不能被中断但仍希望可以接受、处理可能丢失的信号。可靠交付信号的能力直到4.2BSD引入之后(不久被SVR3采用),操作系统才拥有该能力。
:C1hk dBye&|BSD爱好者乐园q_Q WrFG
随着可靠信号的出现,信号的生命和交付(life and delivery)都有所改变。信号可以在之前产生和交付。现在,一旦信号是挂起的(pending),进程可以在接收它之前决定怎么处理。进程可能会去处理它,也可能设置为缺省行为,或者丢弃信号BSD爱好者乐园3[O-Z]+eMc l
BSD爱好者乐园!k(r,m;](ovI
注意:如果许多信号都挂起,系统将会首先交付会改变进程状态的信号,例如SIGBUS。
K!S3e{+_!SlR.K t
6m(y5H.T{'|lBsigprocmask
"L/k xr'\+cT2kc
m Y czv.V任何进程可以使用sigprocmask函数来阻塞信号。语法如下:BSD爱好者乐园YE&N4|*~:Ai!LJ

e c!wJ9~
QUOTE:
int     sigprocmask(int how, const sigset_t *set, sigset_t *oset);
BSD爱好者乐园'{&\ b+c,a-i4WG]
sigprocmask函数会修改或检查(modify or examine)当前信号掩码。当set参数不是null的时候,sigprocmask的行为和第一个参数how有关。函数行为和相关意义列举如下:
E0[+^9hNl ii
N)e{DK6|-U
QUOTE:
SIG_BLOCK: 在set参数中指定的信号被阻塞,并且添加进阻塞信号列表。BSD爱好者乐园 Qc;c"j3r

W+a5r@ru!d}5lESIG_UNBLOCK: 在set参数中指定的信号会从信号掩码中移除。
1Gz e^'dwGE_BSD爱好者乐园1bi zl;J"t
SIG_SETMASK: set参数将完全替代当前信号掩码。如果oset参数不为null,则会被设置为前一个信号掩码。如果set值是null,how参数被忽略并且信号掩码保持不变。所以,为了检查信号掩码,我们可以将传入set null值,oset为非null值来调用sigprocmask函数。一旦掩码得到之后,你可能需要对他进行检查或操作。可以使用下面的过程(routine)。注意当前这些过程是宏的实现。
BSD爱好者乐园 R|M%o1d:}m6SY$OE
 
int    sigemptyset(sigset_t *set)
BSD爱好者乐园;tN(]] C)z'Wz
如果调用这个过程,set参数将被初始化指向一个空信号集。BSD爱好者乐园 R;M?Ddk!E:]

N$AE7j4\1KA]Z1Ou
 
int    sigfillset(sigset_t *set)

?e I ~'h:u如果调用这个过程,set参数将被初始化指向一个包括所有信号的信号集。BSD爱好者乐园 F-|w V#T+w
BSD爱好者乐园FqiBv
 
int   sigaddset(sigset_t *set, int signo)
BSD爱好者乐园~!r,ncr-v-Ij(Sq
如果调用这个过程,signo指定的信号将被添加进set参数指定的信号集。BSD爱好者乐园)t0t2C)hT9H
BSD爱好者乐园BoxtV
 
int    sigdelset(sigset_t *set, int signo)

+O G%V%F,y6`D如果调用这个过程,signo指定的信号将从set参数指定的信号集中移除。
:J [rWzc;vv!s
%r:sVs v
 
int   sigismember(const sigset_t *set, int signo)

%L)lBVC} Dz如果调用这个过程,如果由signo指定的信号存在于set参数指定的信号集中时,返回1,否则返回0。
g*t$l9UA!B?
g8n!FB K,_ho
 
int     sigpending(sigset_t *set);
BSD爱好者乐园%e.h4mY7wg
进程可以使用sigpending函数去查出当前那些信号被挂起。sigpending函数会返回一个包含所有挂起信号的掩码。该掩码可以使用上面介绍的过程去检查。sigpending成功时返回0,否则返回-1,并且设置errno为相应错误值。
)a'[.c;jW9W~7WLm GBSD爱好者乐园J;O2Fz*S1^#am
4.6 自定义行为BSD爱好者乐园9Luk;Ez!gi
一些时候程序要求信号处理句柄运行在一个特定的堆栈上。为了实现这个目的,一个备用(alternate)堆栈区间必须用signaltstack函数指出来。这个函数使用的数据结构为signaltstack:
*n| Zri"[XCBSD爱好者乐园'c6DI;OiM
 
int    sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);

PxNk am/? G I它的结构成员解释如下。BSD爱好者乐园A1p?7Yn%X
BSD爱好者乐园a;NiP d%[-zdF7s3q
 
char    *ss_sp;
BSD爱好者乐园%{A+K9X@8S-a/@
该成员指向一个被用作堆栈的区域。系统中有个MINSIGSTKSZ常量,它定义了进行信号处理时所需的最小内存空间。系统中还有一个SIGSTKSZ常量,它定义了通常情况下处理时所需内存空间。该内存空间需要在调用signaltstack函数之间分配。BSD爱好者乐园A1l b0PS8D4g&n

R8wr"?]8eAv8{'Z`
 
size_t  ss_size;

_8M"Jfjj'|数据成员ss_size指出新堆栈的大小。如果这个值是错误的(inaccurate),当信号处理句柄执行时,它的行为就变得不可预知(你不能明确知道系统怎样处理这个信号)。BSD爱好者乐园l%lzD+W9?dj3P
BSD爱好者乐园2t9D/^#{EzB/T
 
int     ss_flags;
BSD爱好者乐园7]+X [&l-\s8i6{R
根据调用环境(calling circumstances),数据成员ss_flags可以具有少数几个不同的值。首先,当进程希望停用备用堆栈的时候,ss_flags会被设为SS_DISABLE。在这个情况下,ss_sp和ss_size被忽略,备用堆栈被禁止。注意,备用堆栈只能在当前句柄没有处理时禁止。BSD爱好者乐园,z2G8hutq w

,Ur(_1mC4Z2o如果使用一个non-null值作为oss的实参去调用signaltstack,ss_flags将包含指示当前状态的信息。它们是:BSD爱好者乐园uk0wxY+rmc"Wt

g.O y!i?_Z+}Hlp
 
SS_DISABLE: 备用堆栈被停用。BSD爱好者乐园,n@/VJ8f:K
SS_ONSTACK: 备用堆栈当前正在被使用,并且现在不可以停用。
BSD爱好者乐园h C!w'p,z+@f8Z
如果调用signaltstack 的oss实参不是null,会返回当前状态。调用成功返回0,否则返回-1。如果调用失败,errno也会相应的被设置。因为信号可以在任何一点被交付,所以很难被预测。出于这个原因,4.2BSD的缺省行为是:重新开始被中断的系统调用,重新提供还没有被转送的数据。在大多数时候这个行为是很不错的,并且也是所有BSD系统采用的缺省行为。可是,也有一些罕见的情况,你可能需要将这个特性关掉。你可以使用siginterrupt函数完成需求。使用很简单:
*tfQ!`]k$M
1B/w |.P/^ LD
 
int   siginterrupt(int sig, int flag);
BSD爱好者乐园8P sz:}7g4F
将sig参数设置为目标信号,并且设置flag为真(在这个情况下是1)。如果flag参数被设为假(在这个情况下是0),那么缺省行为是重新启动系统调用。
m A"M$`!jD*p
Ak:\pc CC0I

4.7 等待信号

7dma,@a~7dZ.G @BSD爱好者乐园 S1U6}]@9U;[
sigsuspend函数可以暂时将当前阻塞信号集改变为由sigmask指定的信号集。改变后,sigsuspend会等待,直到一个信号被交付。一旦一个信号被交付后,原先的信号集被恢复。由于sigsuspend调用在信号交付之后总是被终止,它的返回值总是-1,errno总是EINTR。下面是它的语法:BSD爱好者乐园f5A2} P*N B'y

+H$vxV*l;a
 
int     sigsuspend(const sigset_t *sigmask);
BSD爱好者乐园zULix?z4Y X
sigwait函数用set参数指定的信号集作为信号掩码。它会检查包含在这个特定集合内的是否有任何挂起信号,如果有的话,它将清除这个挂起的信号,并在sig参数中返回这个被清除信号的数值。如果没有信号挂起,sigwait将一直等待直到指定信号集合中的任何一个信号产生。下面是它的语法:
;Gl W ?/?;y
y&s*Q^$eY |K0j
 
int    sigwait(const sigset_t *set, int *sig);
BSD爱好者乐园I!jd8O[&Q!q
当信号被交付给进程(该进程安装了相应信号的处理句柄)时,进程将会切换到信号处理句柄中执行。例如,假设你的程序监听一个由配置文件设定的端口。你的进程安装了一个捕获SIGHUP信号的处理句柄来重新读取配置文件。一旦SIGHUP信号被交付给你的程序,进程将会执行信号处理句柄来重新读取配置文件。这里存在一个问题,你没有办法知道在进程执行过程中信号被交付的确切地点。你虽然可以使用一些下面列出的函数来将范围缩小,但是如果碰到像打开套接字,打开链接,或者其它那些首先需要清理后才能在新端口上监听 这些情况的时候呢?你怎样确定清理活动在那里开始,什么时候开始?如果你的程序正在等到输入,并且没有数据被传进来的时候,系统调用将被重启(system call will be restarted),所以从SIGHUP的返回将会继续等待。BSD爱好者乐园2[G^ @2}L2E
BSD爱好者乐园MF"MT5?b
这是使用setjmp和longjmp函数的一些情况,这些函数提供非本地分支(non-local branching)。为了使用这些setjmp函数,需要提供一个evn参数,如下:BSD爱好者乐园'p$? @e'F1G9m
BSD爱好者乐园2J[^6e1?QT
 
jmp_buff  env;
0{(A V9v} ]  int    sigsetjmp(sigjmp_buf env, int savemask);BSD爱好者乐园'l)?"IO:tn'rGk
  void   siglongjmp(sigjmp_buf env, int val);BSD爱好者乐园H#O8G]6\7]
  int    setjmp(jmp_buf env);BSD爱好者乐园j]@._Bk;d
  void   longjmp(jmp_buf env, int val);
}4o9T7P7H4@  int     _setjmp(jmp_buf env);BSD爱好者乐园%C!o2or,geI6y.p
  void    _longjmp(jmp_buf env, int val);
!Bo!T.bopc6s7y2U b  void    longjmperror(void);
BSD爱好者乐园`ld u(qv
首先,调用setjmp的返回为0,当前的环境将被保存在env中。接着,你可以在信号处理句柄内部调用对应的longjmp。一旦调用了longjmp,它将把执行环境恢复为env中保存的环境,并返回到最初setjmp被调用时的环境中。最初的setjmp调用返回那个传递给longjmp的va参数的值。BSD爱好者乐园 Y@%~{5aJA)E:Tw,g

9GO+qON关于setjmp和longjmp函数一些说明:首先,这两个是不能混杂使用。也就是说,调用setjmp时保存的env变量不能传递给_longjmp调用。另外,调用setjmp的函数返回后,接下来调用longjmp将会发生错误。
&h$@7O7V[ f?!h|6q8DThe different calls have specific actions that they take. These actions are listed below:
R6q2Okz\S不同的调用有特定的行为。这些行为在下面列出来:
${0s"X2Tu4s)r+W eBSD爱好者乐园WOK!?A$pn ?.P
 
jmp和longjmp: 他们会保存(恢复)信号掩码,寄存器组和堆栈。
BSD爱好者乐园 u6C%B[H Il4Wt

0O_*CF7|d t"C
 
_setjmp和_longjmp: 他们只保存(恢复)寄存器组和堆栈。
BSD爱好者乐园W9|ZQ {i

L&y'HF8c%d%Yh
 
sigsetjmp和siglongjmp: 只要savemask参数不是0,他们就保存(恢复)寄存器组,堆栈和信号掩码。
BSD爱好者乐园| Nz ?%Y5V RQ3f(u
由于一些原因,如果env参数保存的东西被破坏,或者调用setjmp的函数返回了,longjmp函数将会调用longjmperror函数。如果longjmperror也返回了,程序将被异常终止。你可以使用与longjmperror有相同原形的函数来自定义longjmperror函数。缺省的longjmperror会在标准错误上输出”longjmp botch”,然后返回。BSD爱好者乐园)z`0~7JYLE4p1d
4.8 AlarmsBSD爱好者乐园I1B;X)U9tD?w y4W:|
BSD爱好者乐园1FOX'dU+s.s?I4||-x'l
 
unsigned int   alarm(unsigned int seconds);
BSD爱好者乐园gy,]+[!a
alarm函数基本上是一个简单的闹钟时钟(alarm clock),同时也是一个很有用的函数。它允许进程在经过指定秒数之后收到一个通知。一旦闹钟时间到,进程将收到一个SIGALRM信号。任何随后的alarm调用都会覆盖原先的调用设定。alarm不像sleep函数,它不会被阻塞。
6Hr_'re2} RuBSD爱好者乐园NzF&C/_ z@7t}r
它有一些返回值需要值得你注意:首先,如果进程没有设定定时器,那么返回值是0。其次,如果有一个定时器被设定但还没有超时的话,那么会返回前一个调用到现在还有的剩余时间。
a2_W E0`(A
n vy'f(|r现在可以设定的最大时间是100,000,000秒 --- 已经是相当长的时间了。BSD爱好者乐园s u'gF)V+p

/U|T.~)o)B
 
int getitimer(int which, struct itimerval *value);

~4_8h rlS&je Rgetitimer函数会检索由第一个参数(which参数)描述的itimerval结构。第一个参数可选的选项将在下面说明:
\ I*[z/h6@Y
uA8jMa3F \
 
int  setitimer(int which, const struct itimerval *value,  struct itimerval *ovalue);

y8A#EnQ$o5Dsetitmer比先前的alarm调用提供了更稳定的接口。在BSD系统上,每个进程可以提供三种不同时间间隔的定时器。他们在下面讲述:BSD爱好者乐园{9_5tU'[_
BSD爱好者乐园tBXI0d4F$H
 
#define ITIMER_REAL      0
BSD爱好者乐园e l:B*K+n)J
实时时钟实时的递减而不管进程在CPU上的实际花销时间(换句话说,它追踪自然时间natural time)。这允许进程设置一个基于自然实时时间(based on  atural real time)的定时器。当实时定时器超时的时候,进程会收到SIGALRM信号。BSD爱好者乐园$\&Jae%toBf
BSD爱好者乐园}X1on0o;v\xl
 
#define ITIMER_VIRTUAL   1
BSD爱好者乐园&`~E:[9EG/A#p R_
虚拟定时器仅只递减进程在CPU上的执行时间,允许进程设定一个基于CPU使用率的定时器。当虚拟定时器超时的时候,进程收到SIGVTALRM信号BSD爱好者乐园f}+mhf,Vg DQ
BSD爱好者乐园,_2m"_B c*m aj6@
 
#define ITIMER_PROF      2

'hi@DEY _7a.yProfile定时器递减在CPU上的执行时间和代表进程执行的系统调用的时间。这对于那些要求静态剖析的解释程序是很有帮助的。当profile定时器超时的时候,进程收到SIGPROF信号。然而并不像实时和虚拟定时器那样,SIGPROF可以在系统调用的时候被发送;进程应该准备好重新执行被中断的系统调用。
^,tD7e [$ooBSD爱好者乐园qL~v a5E4\d,Y D"{
本章将焦点放在了信号库上。这些信号及他们的使用方法对于系统编程是很重要的。信号允许系统管理员通知应用程序重新读取配置文件,从而使程序更稳定。其它重要的信号处理挂起在打开的文件描述符上的I/O操作。下一章讲述怎样利用这些I/O相关的信号。
[重要提醒]对本篇资料有疑问,请到论坛讨论,尽量使文章准确无误>>>
[版权声明]BSD爱好者乐园站内文章,如来源不是互联网,则均系原创或翻译之作,可随意转载,或以此为基础进行演译,但务必以链接形式注明原始出处和作者信息,否则属于侵权行为。另对本站转载他处文章,俱有说明,如有侵权请联系本人,本人将会在第一时间删除侵权文章。
TAG: freebsd FreeBSD 系统编程
 

评分:0

我来说两句

seccode