zoukankan      html  css  js  c++  java
  • 服务器编程入门(12) 守护进程

    摘要:

        在之前的《服务器编程入门(5)Linux服务器程序规范》中,我们提到过将服务器程序后台化,这就是本节将要讨论的守护进程. 

        本节主要关注一下问题:  

        1 什么是守护进程?

        2 守护进程的启动方法有哪些?

        3 如何创建一个守护进程?


    1 什么是守护进程?

    在后台运行,且不与任何控制终端关联的进程。

    守护进程不与作业控制、终端会话管理、终端产生信号等发生交互,也可以避免在后台运行的守护进程非预期地输出到终端。

    两个特点:

    • 守护进程执行中的信息不显示在任何一个终端上
    • 守护进程不被终端产生的无用信号所中断

    在理解更多关于守护进程的概念之前,我们先了解一下进程、进程组、会话期和控制终端的关系

    • 每一个进程有一个进程ID,每个进程都属于一个进程组
    • 每个进程组有一个组长进程组长进程的ID等于进程组ID
    • 会话期是一个或多个进程组的集合,一个会话期可以有一个单独的控制终端(其中,只有一个前台进程组可以控制终端的交互)
    • 从shell中启动的每个进程将继承一个终端,以便进程与用户交互,同时继承父进程的会话期和进程组ID,因此子进程会受发给该会话期或进程组的信号的影响。

    守护进程与普通进程的区别如下图所示:

    守护进程


    2 守护进程的启动方法有哪些?

    1. 在系统启动阶段,许多守护进程又系统初始化脚本启动。这些脚本通常位于/etc目录或以/etc/rc开头的某个目录中。由这些脚本启动的守护进程一开始时就拥有超级用户特权。
    2. 许多网络服务器由inetd超级服务器启动。
    3. cron守护进程按照规则定期执行一些程序。由它启动执行的程序同样作为守护进程运行。
    4. at命令用于指定将来某个时刻运行程序,由它启动的程序同样作为守护进程。
    5. 从用户终端或前台或后台启动。

    3 使用库函数daemon创建守护进程

    首先我们使用库函数daemon创建守护进程,然后研究一下守护进程的创建过程,并实现一个守护进程化函数,达到和库函数daemon相同的效果。

    函数:daemon

    声明:

    #include <unistd.h>
    int daemon(int nochdir, int noclose);

    作用:通过在服务器程序中调用它,可以把一个普通进程转变为守护进程。

    参数说明:

    If nochdir is zero, daemon()  changes  the  process’s  current  working directory to the root directory ("/"); otherwise,

    If  noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise,  no  changes  are  made  to these file descriptors.

    Demo:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <time.h>
    #include <fcntl.h>
    #include <string.h>
    #include <error.h>
    
    int main(int argc, char* argv[]) {
        time_t t;
        int fd;
        
        // 将当前进程变成守护进程
        if (daemon(0, 0) == -1) {
            perror("daomon error");
            exit(EXIT_FAILURE);
        }
    
        while(1) {
            //这时工作目录已经被daemon函数切换到了系统根目录下
            fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644);   
            if (fd == -1) {
                perror("open daemon.log error");
                exit(EXIT_FAILURE);
            }
            t = time(0);
            char *buf = asctime(localtime(&t));
            write(fd, buf, strlen(buf));     //向daemon.log文件中写入当前时间
            close(fd);
            sleep(60);    // 每隔60s写入一次
        }
    }

    运行截图:

    image

    执行ps命令发现,并没有名为testDaemon的守护进程,主要原因是daemon函数会将当前工作目录切换到/目录下,而普通用户没有权限在系统根目录创建文件。

    所以实际上出错在fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644); 这里,又因为守护进程是不和当前终端交互的,所以没有看到报错信息。

    现在我们切换到root用户执行程序,运行截图:

    image


    4 创建守护进程过程分析,用自己实现的myDaemon函数创建守护进程

    守护进程创建过程:

    守护进程创建流程

    代码实现:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <time.h>
    #include <fcntl.h>
    #include <string.h>
    #include <error.h>
    #include <signal.h>
    
    #define MAXFD 64
    extern int daemon_proc;
    
    int myDaemon(int ,int );
    
    int main(int argc, char* argv[]) {
        time_t t;
        int fd;
        
        if (myDaemon(0, 0) == -1) {
            perror("daomon error");
            exit(EXIT_FAILURE);
        }
    
        while(1) {
            fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644);
            if (fd == -1) {
                perror("open daemon.log error");
                exit(EXIT_FAILURE);
            }
            t = time(0);
            char *buf = asctime(localtime(&t));
            write(fd, buf, strlen(buf));
            close(fd);
            sleep(60);
        }
        fprintf(stderr,
                "Hello world!
    ");
    }
    
    
    int myDaemon(int nochdir, int noclose) {
        int i;
        pid_t pid;
    
        if ( (pid = fork()) < 0 )
            return -1;
        else if (pid) {        /* parent terminated */
            _exit(0);
        }
    
        /* child 1 continues... */
        if (setsid() < 0)     /* become session leader */
            return -1;
    
        signal(SIGHUP, SIG_IGN);    /* ignore SIGHUP singal */
        if ( (pid = fork()) < 0 )
            return -1;
        else if (pid) {
            _exit(0);         /* child 1 terminated */
        }
    
        /* child 2 continues... */
        daemon_proc = 1;      /* use syslog instead of fprintf to stderr */
        
        if (nochdir == 0)
            chdir("/");           /* change working directory */
        
        if (noclose == 0) {
            /*close off file descriptors*/
            for (i = 0; i < MAXFD; i++)
                close(i);
        
            /* redirect stdin, stdout, and stderr to /dev/null */
            open("/dev/null", O_RDONLY);
            open("/dev/null", O_RDWR);    
            open("/dev/null", O_RDWR);
        }
        umask(0);
    }

    运行截图:

    image

    参考资料:

    《UNIX网络编程 卷1:套接字联网API(第3版)》

    linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    Linux内核中的进程组及会话
  • 相关阅读:
    代理模式
    组合模式
    yum配置文件详解
    责任链模式
    git看不到别人创建的远程分支
    学习gulpfile.babel.js随笔
    遍历数组的方法
    解决Error: ENOENT: no such file or directory, scandir 安装node-sass报错
    window对象
    Moment.js的一些用法
  • 原文地址:https://www.cnblogs.com/suzhou/p/daemon.html
Copyright © 2011-2022 走看看