Nginx:进程模型

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx:进程模型相关的知识,希望对你有一定的参考价值。

注:由于源码太长,于是我放另一篇里去了。
《Nginx(3):进程模型》篇 配套代码


进程模型图

main入口伪代码

我来用伪代码简化一下这个入口哈,看看nginx启动的时候都干了些什么。

int ngx_cdecl main(int argc, char *const *argv)
{
    //1、声明一波对象,打印一些基本信息,初始化一些东西

	//2、通过 ngx_save_argv(&init_cycle, argc, argv),将 argv 参数拷贝到一份内存中
    if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {
        return 1;
    }
    /*
			argv 参数是什么?就是管理者想nginx传入的信号,如 nginx -s reload之类的
			在后面还能找到信号捕捉函数,我已经把源码也铺开了,后面再看
		*/

  	//然后继续init

	//3、呐,信号捕捉,再说
    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);
    }

 		//继续配置

#if !(NGX_WIN32)

    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }

    if (!ngx_inherited && ccf->daemon) {
    	// 4、创建守护进程,源码也铺开了,后面再说
        if (ngx_daemon(cycle->log) != NGX_OK) {
            return 1;
        }

        ngx_daemonized = 1;
    }

    if (ngx_inherited) {
        ngx_daemonized = 1;
    }

#endif
	
	//继续配置呗

	//5、启动进程循环咯
    if (ngx_process == NGX_PROCESS_SINGLE) {
        ngx_single_process_cycle(cycle);
    } else {
    		//源码已铺开,同上。
    		//修改进程名,启动worker并管理
        ngx_master_process_cycle(cycle);
    }

    return 0;
}

怎么样,现在局势就很明朗了吧。要想看的那么细比方说初始化了些啥,可以自己去那篇源码里看。


ngx_signal_process 信号捕捉

ngx_save_argv 没啥好看的哈,就一波空间配置并内容拷贝。

Nginx定义ngx_signal_t,用于描述接收到信号时的行为:

typedef struct {
    //信号值
    int     signo;
    //信号名称
    char   *signame;
    //nginx对应的名称,例如reload,reopen,stop等
    char   *name;
    //nginx自定义信号处理函数
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
} ngx_signal_t;

1、在main函数中调用ngx_init_signals,初始化所有需要自定义处理的信号,代码功能很直接,就不放出来了,隔壁有。

2、在master守护进程将所有信号添加到信号集里面。(ngx_master_process_cycle(ngx_cycle_t *cycle))。

3、worker进程将所有信号添加到信号集里面。(ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker))。

4、master向worker发送信号(ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo))。


ngx_daemon 创建守护进程

这里面东西就多了哈。

这个函数学着点,怎么创建一个守护进程。

这段代码不做删减,只做注释,好东西我还是认得出来的

ngx_int_t ngx_daemon(ngx_log_t *log)	
{
    int  fd;

		//要成为守护进程,首先要成为孤儿,进孤儿院
		/*
    	 调用fork函数创建子进程后,使父进程立即退出。产生的子进程将被init进程接管,
    	 同时,所产生的新进程将变为在后台运行。
		*/
    switch (fork()) {
    case -1:
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
        return NGX_ERROR;

    case 0:
        break;

    default:
        exit(0);
    }

    ngx_pid = ngx_getpid();


		/*
			    孤儿调用setsid()函数脱离控制终端和进程组,使该进程成为会话组长,并与原来的登录会话和进程组脱离。
    			此时孤儿进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端。
    			为了避免这种情况,可以通过使进程不再成为会话组长来禁止进程重新打开控制终端,
    			父进程(会话组长)退出,子进程继续执行,并不再拥有打开控制终端的能力。
    			在正在执行的进程中调用INIT_DAEMON后,进程将成为守护进程,脱离控制终端进入后台执行。
		*/
    if (setsid() == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
        return NGX_ERROR;
    }

		/*
			    很多情况下,守护进程会创建一些临时文件。出于安全性的考虑,往往不希望这些文件被别的用户查看。
    			这时,可以使用umask函数修改文件权限,创建掩码的取值,以满足守护进程的要求。
		*/
    umask(0);


		/*
    			新产生的进程从父进程继承了某些打开的文件描述符,如果不使用这些文件描述符,则需要关闭它们。
    			守护进程是运行在系统后台的,不应该在终端有任何的输出信息。
    			可以使用dup函数将标准输入、输出和错误输出重定向到/dev/null设备上
    			(/dev/null是一个空设备,向其写入数据不会有任何输出)。
		*/
    fd = open("/dev/null", O_RDWR);	//难怪在好多地方有看到这么个写法,当时就不知道是干嘛的
    if (fd == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "open(\\"/dev/null\\") failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDIN_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
        return NGX_ERROR;
    }

#if 0
    if (dup2(fd, STDERR_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
        return NGX_ERROR;
    }
#endif

    if (fd > STDERR_FILENO) {
        if (close(fd) == -1) {
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
            return NGX_ERROR;
        }
    }

		/*
			    改变当前工作目录(nginx没有做)
    			使用fork函数产生的子进程将继承父进程的当前工作目录。当进程没有结束时,其工作目录是不能被卸载的。
    			为了防止这种问题发生,守护进程一般会将其工作目录更改到根目录下(/目录)。
		*/
    return NGX_OK;
}


ngx_master_process_cycle 启动master循环

那个孤单进程循环就不说了,人都单身了看个代码还要single吗。。。

void ngx_master_process_cycle(ngx_cycle_t *cycle)
{
   	//声明一大堆对象

    //设置一堆的信号

		//然后统统屏蔽了,因为员工还没来上班

    //然后给进程起个名字

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
		//启动worker进程(再说咯)
    ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);
		//启动文件cache管理进程
    ngx_start_cache_manager_processes(cycle, 0);

   	//初始化一些变量咯
   	ngx_new_binary = 0;
    delay = 0;
    sigio = 0;
    live = 1;
   	//开始循环处理信号咯
    for ( ;; ) {
    				//delay用来设置等待worker进程退出的时间,master接受退出信号后,
				//首先发送退出信号给worker,而worker退出需要一些时间
        if (delay) {
            if (ngx_sigalrm) {
                sigio = 0;
                delay *= 2;
                ngx_sigalrm = 0;
            }

             //日志

           	//设置定时器,以系统真实时间来计算,送出SIGALRM信号
            }
        }

        //···

		//每次处理完一个信号,master进程会被挂起,直到有新的信号到来
        sigsuspend(&set);

  	  	//···

	/*
		子进程退出后,作为父进程的master进程会收到SIGCHLD信号
		发现信号是SIGCHLD后执行ngx_process_get_status函数判断worker子进程是正常退出,还是异常退出
		如果发现worker子进程如果是正常退出的,会将exited置为1
		master进程接收到信号,从挂起状态恢复,继续执行

以上这些,在signal_handle中
	*/
		/*
			发现ngx_reap=1后,ngx_reap_children函数判断是否需要重启worker进程
			如果worker是因为收到了quit信号正常退出的,所有worker进程退出时,live=0
			live=0 并且收到了ngx_quit信号  通过ngx_master_process_exit关闭master进程
		*/
        if (ngx_reap) {
            ngx_reap = 0;
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");

            live = ngx_reap_children(cycle);	//顺便说一下,它会查看所有子进程,因为它也不知道是谁走了
            //在这个函数的第65行,有重启函数
        }

        if (!live && (ngx_terminate || ngx_quit)) {
            ngx_master_process_exit(cycle);
        }


		//如果收到SIGTERM信号或SIGINT信号退出信号,设置推出延迟时间
		//···

		/*
			如果收到SIGQUIT信号
			给所有worker进程发送SIGQUIT信号
			关闭所有监听套接字socket
		*/
     
    	 //如果收到SIGHUP信号,平滑升级就按平滑升级处理,不然就重新配置

       	//and底下一堆的信号处理方案
    }
}


流程图收尾

以上是关于Nginx:进程模型的主要内容,如果未能解决你的问题,请参考以下文章

nginx_2_nginx进程模型

Nginx的工作原理

Nginx-进程模型

Nginx的内部(进程)模型

Nginx-- 进程模型及工作原理

nginx学习:nginx的进程模型