Unix环境高级编程(十四)守护进程实现时间服务器

  守护进程是在后台运行不受终端控制的进程(如输入、输出等),一般的网络服务都是以守护进程的方式运行。守护进程脱离终端的主要原因有两点:(1)用来启动守护进程的终端在启动守护进程之后,需要执行其他任务。(2)(如其他用户登录该终端后,以前的守护进程的错误信息不应出现)由终端上的一些键所产生的信号(如中断信号),不应对以前从该终端上启动的任何守护进程造成影响。要注意守护进程与后台运行程序(即加&启动的程序)的区别。

 创建守护进程的过程:  

1. 调用fork创建子进程。父进程终止,让子进程在后台继续执行。
2. 子进程调用setsid产生新会话期并失去控制终端调用setsid()使子进程进程成为新会话组长和新的进程组长,同时失去控制终端。
3. 忽略SIGHUP信号。会话组长进程终止会向其他进程发该信号,造成其他进程终止。
4. 调用fork再创建子进程。子进程终止,子子进程继续执行,由于子子进程不再是会话组长,从而禁止进程重新打开控制终端。
5. 改变当前工作目录为根目录。一般将工作目录改变到根目录,这样进程的启动目录也可以被卸掉。
6. 关闭打开的文件描述符,打开一个空设备,并复制到标准输出和标准错误上。 避免调用的一些库函数依然向屏幕输出信息。
7. 重设文件创建掩码清除从父进程那里继承来的文件创建掩码,设为0。
8. 用openlog函数建立与syslogd的连接。

创建守护进程的例子如下程序所示:

复制代码
void daemon_init(const char* pname,int facility)
{
    int                 i;
    pid_t               pid;
    struct  rlimit      rl;
    struct  sigaction   sa;
    /* 清除文件模式创建掩码,使新文件的权限位不受原先文件模式创建掩码的权限位的影响*/
    umask(0);
    if(getrlimit(RLIMIT_NOFILE,&rl) < 0)
    {
        perror("getrlimit() error");
        exit(-1);
    }
    if((pid = fork()) < 0)
    {
        perror("fork() error");
        exit(-1);
    }
    else if(pid > 0)   /*父进程终止 */
        exit(0);
    setsid();         /* 子进程成为会话首进程*/
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if(sigaction(SIGHUP,&sa,NULL) < 0)
    {
        perror("sigaction() error");
        exit(-1);
    }
    if((pid = fork()) < 0)
    {
        perror("fork() error");
        exit(-1);
    }
    else if(pid > 0)
        exit(0);        /* 第一个子程进终止,保证后面操作不会分配终端 */
    if(chdir("/")<0)    /* 改变工作目录 */
    {
        perror("chdir() error");
        exit(-1);
    }
    if(rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max = 1024;
    for(i=0;i<rl.rlim_max;++i)  /*关闭所有打开的文件描述字*/
        close(i);
    openlog(pname, LOG_PID, facility);  /*用syslogd处理错误*/
}
复制代码

现在要用守护进程实现一个时间服务器,呈现的功能是:服务器运行后自动成为守护进程,返回shell;客户端运行后收到服务器发来的当前时间。

时间服务器程序(timeserver.c)如下:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <errno.h>
  6 #include <sys/types.h>
  7 #include <fcntl.h>
  8 #include <signal.h>
  9 #include <syslog.h>
 10 #include <sys/resource.h>
 11 #include <sys/socket.h>
 12 #include <netinet/in.h>
 13 #include <arpa/inet.h>
 14 #include <time.h>
 15 
 16 #define MAXLINE 100
 17 
 18 void daemon_init(const char* pname,int facility)
 19 {
 20     int                 i;
 21     pid_t               pid;
 22     struct  rlimit      rl;
 23     struct  sigaction   sa;
 24     /* 清除文件模式创建掩码,使新文件的权限位不受原先文件模式创建掩码的权限位的影响*/
 25     umask(0);
 26     if(getrlimit(RLIMIT_NOFILE,&rl) < 0)
 27     {
 28         perror("getrlimit() error");
 29         exit(-1);
 30     }
 31     if((pid = fork()) < 0)
 32     {
 33         perror("fork() error");
 34         exit(-1);
 35     }
 36     else if(pid > 0)   /*父进程终止 */
 37         exit(0);
 38     setsid();         /* 子进程成为会话首进程*/
 39     sa.sa_handler = SIG_IGN;
 40     sigemptyset(&sa.sa_mask);
 41     sa.sa_flags = 0;
 42     if(sigaction(SIGHUP,&sa,NULL) < 0)
 43     {
 44         perror("sigaction() error");
 45         exit(-1);
 46     }
 47     if((pid = fork()) < 0)
 48     {
 49         perror("fork() error");
 50         exit(-1);
 51     }
 52     else if(pid > 0)
 53         exit(0);        /* 第一个子程进终止,保证后面操作不会分配终端 */
 54     if(chdir("/")<0)    /* 改变工作目录 */
 55     {
 56         perror("chdir() error");
 57         exit(-1);
 58     }
 59     if(rl.rlim_max == RLIM_INFINITY)
 60         rl.rlim_max = 1024;
 61     for(i=0;i<rl.rlim_max;++i)  /*关闭所有打开的文件描述字*/
 62         close(i);
 63     openlog(pname, LOG_PID, facility);  /*用syslogd处理错误*/
 64 }
 65 
 66 int main(int argc,char *argv[])
 67 {
 68     int                 listenfd, connfd;
 69     socklen_t           addrlen, len;
 70     struct sockaddr     cliaddr;
 71     struct sockaddr_in  server;
 72     char                buff[MAXLINE];
 73     time_t              ticks;
 74     int                 n;
 75     bzero(&server, sizeof(server));
 76     bzero(&cliaddr,sizeof(cliaddr));
 77     server.sin_family = AF_INET;
 78     server.sin_port = htons(5050);
 79     server.sin_addr.s_addr = htonl(INADDR_ANY);
 80     daemon_init(argv[0], 0);
 81     if((listenfd=socket(AF_INET, SOCK_STREAM, 0))==-1)
 82     {
 83         syslog(LOG_NOTICE|LOG_LOCAL0,"socket error");
 84         exit(-1);
 85     }
 86     if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr))==-1)
 87     {
 88         syslog(LOG_NOTICE|LOG_LOCAL0,"socket error");
 89         exit(-1);
 90     }
 91     if(listen(listenfd,5)==-1)
 92     {
 93         syslog(LOG_NOTICE|LOG_LOCAL0,"listen error");
 94         exit(-1);
 95     }
 96     for ( ; ; )
 97     {
 98         len = sizeof(cliaddr);
 99         connfd = accept(listenfd,&cliaddr, &len);
100         ticks = time(NULL);
101         snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
102         if((n= write(connfd, buff, strlen(buff)))==-1)
103            syslog(LOG_NOTICE|LOG_LOCAL0,"write error");
104         close(connfd);
105     }
106 }

客户端程序(timeclient.c)如下:

 1 #include <unistd.h>
 2 #include <sys/socket.h>
 3 #include <netinet/in.h>
 4 #include <netdb.h>
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <string.h>
 8 #define PORT            5050
 9 #define MAXDATASIZE     100
10 
11 int main(int argc, char *argv[])
12 {
13     int                  fd, numbytes;
14     char                 buf[MAXDATASIZE];
15     struct sockaddr_in   server;
16     if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
17     {
18         perror("Create socket failed.");
19         exit(-1);
20     }
21     bzero(&server, sizeof(server));
22     server.sin_family = AF_INET;
23     server.sin_port = htons(PORT);
24     server.sin_addr.s_addr = htonl(INADDR_ANY);
25     if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1)
26     {
27         perror("connect failed.");
28         exit(-1);
29     }
30     if( ((numbytes = recv(fd, buf, MAXDATASIZE, 0)) == -1))
31     {
32         perror("recv error.");
33         exit(-1);
34     }
35     buf[numbytes] ='\0';
36     printf("Server Message: %s\n",buf);
37     close(fd);
38 }

程序运行过程: 先运行时间服务器程序,再在运行客户端程序。

运行时间服务器程序结果如下:

此时timeserver是个守护进程,已经在运行,通过ps -axj命令可以查看该进程,查看结果如下:

 运行客户端程序结果如下:

客户端收到服务器发送的当前时间。

posted on 2018-02-24 15:45  AlanTu  阅读(304)  评论(0编辑  收藏  举报

导航