c、执行系统的默认动作。对绝大多数信号而言,系统的默认动作都是终止该进程。
在Linux下,信号有很多种,我在这里就不一一介绍了,假如想周详地对这些信号进行了解,能够查看头文档<sigal.h>,这些信号都被定义为正整数,也就是他们的信号编号。在对信号进行处理时,必须要用到函数signal(),此函数的周详描述为:
-----------------------------------------------------------------
#include <signal.h>
void (*signal (int signo, void (*func)(int)))(int);
-----------------------------------------------------------------
其中参数signo为信号名,参数func的值根据我们的需要能够是以下几种情况:(1)常数SIG_DFL,表示执行系统的默认动作。(2)常数SIG_IGN,表示忽略信号。(3)收到信号后需要调用的处理函数的地址,此信号捕获程式应该有一个整型参数但是没有返回值。signal()函数返回一个函数指针,而该指针指向的函数应该无返回值(void),这个指针其实指向以前的信号捕获程式。
下面 回到我们的daemonize()函数上来。这个函数在创建守护进程时忽略了三个信号:
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
这三个信号的含义分别是:SIGTTOU表示后台进程写控制终端,SIGTTIN表示后台进程读控制终端,SIGTSTP表示终端挂起。
4.关闭不再需要的文档描述符,并为标准输入、标准输出和标准错误输出打开新的文档描述符(也能够继承父进程的标准输入、标准输出和标准错误输出文档描述符,这个操作是可选的)。在我们这段例程中,因为是代理服务器程式,而且是在执行了listen()函数之后执行这个daemonize()的,所以要保留已转换成功的倾听套接字,所以我们能够见到这样的语句:
if (fd != servfd)
close(fd);
5.调用函数chdir("/")将当前工作目录更改为根目录。这是为了确保我们的进程不使用任何目录。否则我们的守护进程将一直占用某个目录,这可能会造成终极用户不能卸载一个文档系统。
6.调用函数umask(0)将文档方式创建屏蔽字配置为"0"。这是因为由继承得来的文档创建方式屏蔽字可能会禁止某些许可权。例如我们的守护进程需要创建一组可读可写的文档,而此守护进程从父进程那里继承来的文档创建方式屏蔽字却有可能屏蔽掉了这两种许可权,则新创建的一组文档其读或写操作就不能生效。因此要将文档方式创建屏蔽字配置为"0"。
在daemonize()函数的最后,我们能够看到这样的信号捕获处理语句:
signal(SIGCLD,(Sigfunc *)reap_status);
这不是创建守护进程过程中必须的一步,他的作用是调用我们自定义的reap_status()函数来处理僵死进程。reap_status()在例程中的定义为:
-----------------------------------------------------------------
/****************************************************************
function: reap_status
description: handle a SIGCLD signal by reaping the exit status of the perished child, and discarding it.
arguments: none.
return value: none.
calls: none.
globals: none.
****************************************************************/
void reap_status()
{
int pid;
union wait status;
while ((pid = wait3(&status,WNOHANG,NULL)) > 0)
; /* loop while there are more dead children */
}
-----------------------------------------------------------------
上面信号捕获语句的原文为:
signal(SIGCLD, reap_status);
我们刚才说过,signal()函数的第二个参数一定要有有一个整型参数但是没有返回值。而reap_status()是没有参数的,所以原来的语句在编译时无法通过。所以我在预编译部分加入了对Sigfunc()的类型定义,在这里用做对reap_status进行强制类型转换。而且在BSD系统中通常都使用SIGCHLD信号来处理子进程终止的有关信息,SIGCLD是System V中定义的一个信号名,假如将SIGCLD信号的处理方式设定为捕获,那么内核将马上检查系统中是否存在已终止等待处理的子进程,假如有,则立即调用信号捕获处理程式。
一般在信号捕获处理程式中都要调用wait()、waitpid()、wait3()或是wait4()来返回子进程的终止状态。这些"等待"函数的区别是,当需要函数"等待"的子进程还没有终止时,wait()将使其调用者阻塞;而在waitpid()的参数中能够设定使调用者不发生阻塞,wait()函数不被配置为等待哪个具体的子进程,他等待调用者任何子进程中首先终止的那个,而在调用waitpid()时却必须在参数中设定被等待的子进程ID。而wait3()和wait4()的参数分别比wait()和waitpid()还要多一个"rusage"。例程中的reap_status()就调用了函数wait3(),这个函数是BSD系统支持的,我们把他和wait4()的定义一起列出来:
-----------------------------------------------------------------
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
pid_t wait3(int *statloc, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);
-----------------------------------------------------------------
其中指针statloc假如不为"NULL",那么他将指向返回的子进程终止状态。参数pid是我们指定的被等待的子进程的进程ID。参数options是我们的控制选择项,一般为WNOHANG或是WUNTRACED。例程中使用了选项WNOHANG,意即假如不能立即返回子进程的终止状态(譬如由于子进程还未结束),那么等待函数不阻塞,此时返回"0"。 WUNTRACED选项的意思是假如系统支持作业控制,假如要等待的子进程的状态已暂停,而且其状态自从暂停以来还从未报告过,则返回其状态。参数rusage假如不为"NULL",则他将指向内核返回的由终止进程及其任何子进程使用的资源摘要,该摘要包括用户CPU时间总量、缺页次数、接收到信号的次数等。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




