Chinaunix首页 | 论坛 | 博客
  • 博客访问: 313811
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 493
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-14 17:08
文章分类

全部博文(144)

文章存档

2015年(42)

2014年(19)

2013年(83)

我的朋友

分类: LINUX

2014-08-12 14:50:00

nginx源码分析(12)-进程启动分析(2)              
 
第四个剖析的是worker子进程的执行函数ngx_worker_process_cycle(src/os/unix/ngx_process_cycle.c)。

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
    ngx_uint_t         i;
    ngx_connection_t  *c;

    // 初始化
    ngx_worker_process_init(cycle, 1);

    ngx_setproctitle("worker process");

#if (NGX_THREADS)
   // 略去关于线程的代码
#endif

    for ( ;; ) {

        // 退出状态已设置,关闭所有连接
        if (ngx_exiting) {

            c = cycle->connections;

            for (i = 0; i < cycle->connection_n; i++) {

                /* THREAD: lock */

                if (c[i].fd != -1 && c[i].idle) {
                    c[i].close = 1;
                    c[i].read->handler(c[i].read);
                }
            }

            if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
            {
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

                ngx_worker_process_exit(cycle);
            }
        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

        // 处理事件和计时
        ngx_process_events_and_timers(cycle);

        // 收到NGX_CMD_TERMINATE命令
        if (ngx_terminate) {
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

            // 清理后进程退出,会调用所有模块的钩子exit_process
            ngx_worker_process_exit(cycle);
        }

        // 收到NGX_CMD_QUIT命令
        if (ngx_quit) {
            ngx_quit = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                          "gracefully shutting down");
            ngx_setproctitle("worker process is shutting down");

            if (!ngx_exiting) {
                // 关闭监听socket,设置退出状态
                ngx_close_listening_sockets(cycle);
                ngx_exiting = 1;
            }
        }

        // 收到NGX_CMD_REOPEN命令,重新打开log
        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, -1);
        }
    }
}


worker子进程的初始化函数是ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority),这个函数可分解为以下步骤:

1、设置ngx_process = NGX_PROCESS_WORKER,在master进程中这个变量被设置为NGX_PROCESS_MASTER;

2、全局性的设置,根据全局的配置信息设置执行环境、优先级、限制、setgid、setuid、信号初始化等;

3、调用所有模块的钩子init_process;

4、关闭不使用的socket,关闭当前worker的channel[0]句柄和其他worker的channel[1]句柄,当前worker会使用其他worker的channel[0]句柄发送消息,使用当前worker的channel[1]句柄监听可读事件:

    for (n = 0; n < ngx_last_process; n++) {

        if (ngx_processes[n].pid == -1) {
            continue;
        }    

        if (n == ngx_process_slot) {
            continue;
        }    

        if (ngx_processes[n].channel[1] == -1) {
            continue;
        }    

        if (close(ngx_processes[n].channel[1]) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "close() channel failed");
        }    
    }    

    if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "close() channel failed");
    }    

5、在当前worker的channel[1]句柄监听可读事件:

    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
                              ngx_channel_handler)
        == NGX_ERROR)
    {
        /* fatal */
        exit(2);
    }
ngx_add_channel_event把句柄ngx_channel(当前worker的channel[1])上建立的连接的可读事件加入事件监控队列,事件处理函数为ngx_channel_hanlder(ngx_event_t *ev)。当有可读事件的时候,ngx_channel_handler负责处理消息,过程如下:

static void 
ngx_channel_handler(ngx_event_t *ev) 
{
    ngx_int_t          n;   
    ngx_channel_t      ch;  
    ngx_connection_t  *c;  

    if (ev->timedout) {
        ev->timedout = 0; 
        return;
    }    

    c = ev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler");


    for ( ;; ) {

        // 从channel[1]中读取消息
        n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n);

        if (n == NGX_ERROR) {

            if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
                ngx_del_conn(c, 0);
            }

            ngx_close_connection(c);
            return;
        }

        if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
            if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return;
            }
        }

        if (n == NGX_AGAIN) {
            return;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
                       "channel command: %d", ch.command);

        // 处理消息命令
        switch (ch.command) {

        case NGX_CMD_QUIT:
            ngx_quit = 1;
            break;

        case NGX_CMD_TERMINATE:
            ngx_terminate = 1;
            break;

        case NGX_CMD_REOPEN:
            ngx_reopen = 1;
            break;

        case NGX_CMD_OPEN_CHANNEL:

            ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,
                           "get channel s:%i pid:%P fd:%d",
                           ch.slot, ch.pid, ch.fd);

            ngx_processes[ch.slot].pid = ch.pid;
            ngx_processes[ch.slot].channel[0] = ch.fd;
            break;

        case NGX_CMD_CLOSE_CHANNEL:

            ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,
                           "close channel s:%i pid:%P our:%P fd:%d",
                           ch.slot, ch.pid, ngx_processes[ch.slot].pid,
                           ngx_processes[ch.slot].channel[0]);

            if (close(ngx_processes[ch.slot].channel[0]) == -1) {
                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
                              "close() channel failed");
            }

            ngx_processes[ch.slot].channel[0] = -1;
            break;
        }
    }


nginx的启动过程就分析完了,上面分析的只是启动过程的主要逻辑分支,这个过程中和每个模块相关的细节留待后续按功能分析的时候再剖析。

简单总结一下。

nginx的启动过程可以划分为两个部分,第一部分是读取配置文件并设置全局的配置结构信息以及每个模块的配置结构信息,这期间会调用模块的create_conf钩子和init_conf钩子;第二部分是创建进程和进程间通信机制,master进程负责管理各个worker子进程,通过socketpair向子进程发送消息,各个worker子进程服务利用事件机制处理请求,通过socketpair与其他子进程通信(发送消息或者接收消息),进程启动的各个适当时机会调用模块的init_module钩子、init_process钩子、exit_process钩子和exit_master钩子,init_master钩子没有被调用过。

nginx的全局变量非常多,这些全局变量作用不一,有些是作为全局的配置信息,比如ngx_cycle;有些是作为重要数据的全局缓存,比如ngx_processes;有些是状态信息,比如ngx_process,ngx_quit。很难对所有的全局变量作出清晰的分类,而且要注意的是,子进程继承了父进程的全局变量之后,子进程和父进程就会独立处理这些全局变量,有些全局量需要在父子进程之间同步就要通过通信方式了,比如ngx_processes(进程表)的同步。
阅读(1376) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~