daemon_init函数
下面给出名为daemon_init函数,通过调用它(通常从服务器程序中),我们能够把一个普通进程转变为守护进程
1 #include "unp.h" 2 #include <syslog.h> 3 4 #define MAXFD 64 5 6 extern int daemon_proc; /* defined in error.c */ 7 8 int 9 daemon_init(const char *pname, int facility) 10 { 11 int i; 12 pid_t pid; 13 14 if ( (pid = Fork()) < 0) 15 return (-1); 16 else if (pid) 17 _exit(0); /* parent terminates */ 18 19 /* child 1 continues... */ 20 21 if (setsid() < 0) /* become session leader */ 22 return (-1); 23 24 Signal(SIGHUP, SIG_IGN); 25 if ( (pid = Fork()) < 0) 26 return (-1); 27 else if (pid) 28 _exit(0); /* child 1 terminates */ 29 30 /* child 2 continues... */ 31 32 daemon_proc = 1; /* for err_XXX() functions */ 33 34 chdir("/"); /* change working directory */ 35 36 /* close off file descriptors */ 37 for (i = 0; i < MAXFD; i++) 38 close(i); 39 40 /* redirect stdin, stdout, and stderr to /dev/null */ 41 open("/dev/null", O_RDONLY); 42 open("/dev/null", O_RDWR); 43 open("/dev/null", O_RDWR); 44 45 openlog(pname, LOG_PID, facility); 46 47 return (0); /* success */ 48 }
作为守护进程运行的时间获取服务器程序
1 #include "unp.h" 2 #include <time.h> 3 4 int 5 main(int argc, char **argv) 6 { 7 int listenfd, connfd; 8 socklen_t addrlen, len; 9 struct sockaddr *cliaddr; 10 char buff[MAXLINE]; 11 time_t ticks; 12 13 if (argc < 2 || argc > 3) 14 err_quit("usage: daytimetcpsrv2 [ <host> ] <service or port>"); 15 16 daemon_init(argv[0], 0); 17 18 if (argc == 2) 19 listenfd = Tcp_listen(NULL, argv[1], &addrlen); 20 else 21 listenfd = Tcp_listen(argv[1], argv[2], &addrlen); 22 23 cliaddr = Malloc(addrlen); 24 25 for ( ; ; ) { 26 len = addrlen; 27 connfd = Accept(listenfd, cliaddr, &len); 28 err_msg("connection from %s", Sock_ntop(cliaddr, len)); 29 30 ticks = time(NULL); 31 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); 32 Write(connfd, buff, strlen(buff)); 33 34 Close(connfd); 35 } 36 }
inetd守护进程
典型的UNIX系统可能存在许多服务器,它们只是等待客户请求的到达,例如:FTP、Telnet、Rlogin、TFTP等等。
在之前,所有这些服务都有一个进程与之关联,而且每个进程执行几乎相同的启动任务。这个模型存在以下两个问题:
1.所有这些守护进程含有几乎相同的启动代码。
2.每个守护进程在进程表中占据一个表项,然后它们大部分时间处于睡眠状态。
4.3BSD版本基于TCP或UDP的服务器都可以使用inetd守护进程来解决上述两个问题:
1.通过由inetd处理普通守护进程的大部分启动细节以简化守护进程的编写。
2.单个进程(inetd)就能为多个服务等待外来的客户请求,以此取代每个服务一个进程的做法。
inetd进程使用我们随daemon_init函数讲解的技巧把自己演变成一个守护进程。
接着读入并处理自己的配置文件。通常是/etc/inetd.conf的配置文件制定本超级服务器处理哪些服务以及当一个服务请求到达时该怎么做。该文件中每行包含的字段如下图所示:
下面是inetd.conf文件中作为例子的若干行:
下面展示inetd守护进程的工作流程
1.在启动阶段,读入/etc/inetd.conf文件并给该文件中指定的每个服务创建一个适当类型的套接字。新创建的每个套接字都被加入到将由某个select调用使用的一个描述符。
2.位每个套接字调用bind,指定捆绑相应服务器的总所周知的端口和通配地址。端口号通过getservbyname获得。
3.对于每个TCP套接字,调用listen以接收外来的连接请求。对于UDP套接字则不执行该步骤。
4.创建完毕所有套接字之后,调用select等待其中任何一个套接字变为可读。
5.当select返回指出某个套接字可读之后,如果该套接字时一个TCP套接字,而且其服务器的wait-flag值为nowait,那么调用accept接受这个新连接。
6.inetd守护进程调用fork派生进程,并由子进程处理服务请求。(子进程首先与终端脱离,然后调用exec执行相应的server-program字段指定的程序来具体处理请求)
7.如果第五步中select返回的是一个字节流套接字,那么父进程必须关闭已连接套接字(就像标准并发服务器那样)。父进程再次调用select,等待下一个变为可读的套接字。
daemon_inetd函数
下面给出一个名为daemon_inetd的函数,可用于已知由inetd启动的服务器程序中。
1 #include "unp.h" 2 #include <syslog.h> 3 4 extern int daemon_proc; /* defined in error.c */ 5 6 void 7 daemon_inetd(const char *pname, int facility) 8 { 9 daemon_proc = 1; /* for our err_XXX() functions */ 10 openlog(pname, LOG_PID, facility); 11 }
与daemon_init相比,所有的守护进程化步骤已由inetd在启动时执行。本函数的任务仅仅是为错误处理函数设置daemon_proc标志,并调用openlog
下面给出由inetd作为守护进程启动的时间获取服务器程序
1 #include "unp.h" 2 #include <time.h> 3 4 int 5 main(int argc, char **argv) 6 { 7 socklen_t len; 8 struct sockaddr *cliaddr; 9 char buff[MAXLINE]; 10 time_t ticks; 11 12 daemon_inetd(argv[0], 0); 13 14 cliaddr = Malloc(sizeof(struct sockaddr_storage)); 15 len = sizeof(struct sockaddr_storage); 16 Getpeername(0, cliaddr, &len); 17 err_msg("connection from %s", Sock_ntop(cliaddr, len)); 18 19 ticks = time(NULL); 20 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); 21 Write(0, buff, strlen(buff)); 22 23 Close(0); /* close TCP connection */ 24 exit(0); 25 }
可以看到,所有套接字创建代码都消失了。这些步骤改由inetd执行。
为了运行本例子的程序,我们首先赋予本服务一个名字和端口,将如下行加到/etc/services文件中:
mydaytime 9999/tcp
接着把如下行加到/etc/inetd.conf文件中:
mydaytime stream tcp nowait andy /home/andy/daytimetcpsrv3 daytimetcpsrv3
然后给inetd发送一个SIGHUP信号,告知它重新读入其配置文件。
接着我们执行netstat命令验证inetd已在TCP端口9999上创建一个监听套接字