zoukankan      html  css  js  c++  java
  • 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
    ", 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] ='';
    36     printf("Server Message: %s
    ",buf);
    37     close(fd);
    38 }

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

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

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

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

    客户端收到服务器发送的当前时间。
  • 相关阅读:
    HUD 问题
    嵌入式面试
    网上某人面试经验总结
    C中prngtf是从右到左压栈的
    哈希表
    做事原则
    学习单片机的步骤
    C#预处理器命令
    CWinApp类CMultiDocTemplate类CDocument类CView类的关系
    Windows消息大全
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8466129.html
Copyright © 2011-2022 走看看