1

Nginx 源码阅读(一)

 2 years ago
source link: https://jiajunhuang.com/articles/2022_03_21-nginx_source_code_1.md.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Nginx 源码阅读(一)

去年读了一部分Nginx源码,后来耽搁了,最近决定捡起来做完这件事情。说起Nginx源码, 我主要是想了解Nginx的代码组织、架构、master/worker分工、以及一个请求的处理流程。 对其它的我就不是那么感兴趣了。因此这一系列Nginx源码阅读也主要是围绕这些话题来写的。

我读的是 Nginx 0.1.0

首先把代码拉下来,然后切到 release-0.1.0 这个tag,这就是我要读的源码。 要了解一份代码,首先我们要了解它的组织结构,然后我们要找到入口,顺着入口一路跟, 通常来说,入口就是 main 函数。

先来看看源码组织:

$ tree -d 
.
├── auto
│   ├── fmt
│   ├── lib
│   │   ├── md5
│   │   ├── openssl
│   │   ├── pcre
│   │   └── zlib
│   ├── os
│   └── types
├── conf
├── docs
│   ├── dtd
│   ├── html
│   ├── text
│   ├── xml
│   │   └── nginx
│   ├── xsls
│   └── xslt
├── objs
│   └── src
│       ├── core
│       ├── event
│       │   └── modules
│       ├── http
│       │   └── modules
│       │       └── proxy
│       ├── imap
│       └── os
│           ├── unix
│           └── win32
└── src
    ├── core
    ├── event
    │   └── modules
    ├── http
    │   └── modules
    │       └── proxy
    ├── imap
    └── os
        ├── unix
        └── win32

41 directories

首先 docs, conf, objs 目录我们不管,分别是文档、配置文件和编译出来的目标 文件存放处。auto 目录下是各种编译脚本,src 下就是源码。

src 下主要是5个子目录:

  • core 一些基本类型和各种数据结构、函数,比如 string, array, pool
  • event 事件库封装,epoll, kqueue, select
  • http HTTP相关的所有代码和模块都在这里
  • imap 邮件相关,我们忽略
  • os 操作系统相关的函数封装

由上可知,我们主要的关注点都将落在 core, http 这两个文件夹下,其它的我们 就在要用到的时候,再去考察。

接下来就是找到 main 函数,这个简单,搜索一下便是。

$ ag 'int main'
event/ngx_event_connectex.c
62:int ngx_iocp_new_thread(int main)
120:void ngx_iocp_wait_events(int main)

core/nginx.c
99:int main(int argc, char *const *argv)

启动流程分析

找到了 main 函数,接下来要做的事情就是跟着 main 函数一步一步看启动流程,不过我读源码的时候,还需要用gdb 定位一些函数的位置,所以我费了一些时间在本地把Nginx 0.1.0 编译起来,十几年前的代码,还是需要做一些修改才能 跑起来的。我们来看 main 函数:

int main(int argc, char *const *argv)
{
    // 声明一些变量
    ngx_int_t          i;
    ngx_log_t         *log;
    ngx_cycle_t       *cycle, init_cycle;
    ngx_core_conf_t   *ccf;
    ngx_master_ctx_t   ctx;

    /* TODO */ ngx_max_sockets = -1;

    ngx_time_init(); // 跟踪代码看了一下,是初始化Nginx内部缓存的时间变量

#if (HAVE_PCRE)
    ngx_regex_init(); // 这个很明显,初始化PCRE,也就是正则表达式
#endif

    ngx_pid = ngx_getpid(); // 获取进程ID

    if (!(log = ngx_log_init_stderr())) {
        return 1;
    }

#if (NGX_OPENSSL)
    ngx_ssl_init(log); // 初始化SSL
#endif

    /* init_cycle->log is required for signal handlers and ngx_getopt() */
    // 初始化 cycle。cycle是一个很重要的变量,简单理解,就是Nginx各种乱七八糟的运行时上下文都存在这里。
    // 参考 https://nginx.org/en/docs/dev/development_guide.html#cycle
    ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
    init_cycle.log = log;
    ngx_cycle = &init_cycle;

    ngx_memzero(&ctx, sizeof(ngx_master_ctx_t));
    ctx.argc = argc;
    ctx.argv = argv;

    // 内存池:https://nginx.org/en/docs/dev/development_guide.html#pool
    if (!(init_cycle.pool = ngx_create_pool(1024, log))) {
        return 1;
    }

    if (ngx_getopt(&ctx, &init_cycle) == NGX_ERROR) {
        return 1;
    }

    if (ngx_test_config) {
        log->log_level = NGX_LOG_INFO;
    }

    // src/os/unix/ngx_linux_init.c 初始化一些系统相关的东西,我的系统是Linux,所以调用 ngx_linux_init.c
    if (ngx_os_init(log) == NGX_ERROR) {
        return 1;
    }

    // 这里是从环境变量里提取要继承的fd
    if (ngx_add_inherited_sockets(&init_cycle) == NGX_ERROR) {
        return 1;
    }

    ngx_max_module = 0;
    // ngx_modules 在 objs/ngx_modules.c 里
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = ngx_max_module++;
    }

    // 初始化 cycle。里面会初始化各个模块
    cycle = ngx_init_cycle(&init_cycle);
    if (cycle == NULL) {
        if (ngx_test_config) {
            ngx_log_error(NGX_LOG_EMERG, log, 0,
                          "the configuration file %s test failed",
                          init_cycle.conf_file.data);
        }

        return 1;
    }

    if (ngx_test_config) {
        ngx_log_error(NGX_LOG_INFO, log, 0,
                      "the configuration file %s was tested successfully",
                      init_cycle.conf_file.data);
        return 0;
    }

    ngx_os_status(cycle->log);

    ngx_cycle = cycle;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_process = ccf->master ? NGX_PROCESS_MASTER : NGX_PROCESS_SINGLE;

    if (ngx_create_pidfile(cycle, NULL) == NGX_ERROR) {
        return 1;
    }

    if (ngx_process == NGX_PROCESS_MASTER) {
        // master/worker 模式下要执行的代码
        ngx_master_process_cycle(cycle, &ctx);
    } else {
        // 单进程模式下要执行的代码
        ngx_single_process_cycle(cycle, &ctx);
    }

    return 0;
}

看到这里,我们大概知道Nginx是怎么启动的,首先各种初始化,然后判断是否是 master/worker 模式,是的话,就执行 ngx_master_process_cycle 函数去处理,但是我们还不知道具体里面发生了什么,所以继续跟下去。

master 是如何工作的

void ngx_master_process_cycle(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx)
{
    char              *title;
    u_char            *p;
    size_t             size;
    ngx_int_t          i;
    sigset_t           set;
    struct timeval     tv;
    struct itimerval   itv;
    ngx_uint_t         live;
    ngx_msec_t         delay;
    ngx_core_conf_t   *ccf;

    // 设置感兴趣的信号,信号处理函数已经在 ngx_os_init 里设置了
    sigemptyset(&set);
    // ...

    // 设置进程名
    ngx_setproctitle(title);


    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);

    ngx_new_binary = 0;
    delay = 0;
    live = 1;

    for ( ;; ) {
        sigsuspend(&set);

        ngx_gettimeofday(&tv);
        ngx_time_update(tv.tv_sec);

        // 然后根据全局变量来执行对应动作

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

        if (ngx_reap) {
            // ...
        }

        if (!live && (ngx_terminate || ngx_quit)) {
            // ...
        }

        if (ngx_terminate) {
            // ...
        }

        if (ngx_quit) {
            // ...
        }

        if (ngx_timer) {
            // ...
        }

        if (ngx_reconfigure) {
            // ...
        }

        if (ngx_restart) {
            // ...
        }

        if (ngx_reopen) {
            // ...
        }

        if (ngx_change_binary) {
            // ...
        }

        if (ngx_noaccept) {
            // ...
        }
    }
}

现在我们知道了,master其实不会处理任何连接。master首先进行初始化,设置好感兴趣的信号,然后创建worker,之后master本身 就进入一个无限循环,通过 sigsuspend 函数阻塞自身,当收到信号时,通过几个全局变量来决定自己的行为,例如重启,rotate 日志,退出等等。

sig_atomic_t  ngx_reap;
sig_atomic_t  ngx_timer;
sig_atomic_t  ngx_sigio;
sig_atomic_t  ngx_terminate;
sig_atomic_t  ngx_quit;
sig_atomic_t  ngx_reconfigure;
sig_atomic_t  ngx_reopen;
sig_atomic_t  ngx_change_binary;
sig_atomic_t  ngx_noaccept;

Nginx通过使用这几个原子变量来指示 master 收到信号以后,应该如何处理。我们在很多Go的代码里,也看到过类似的逻辑,例如 用一个 atomic 来存储 exit 以指示是否要退出(以前看 Thrift Go 实现就有这样的用法)。

那么,master 是怎么改变这些变量的呢?答案其实就是通过信号。我们上面只看到了 master 设置感兴趣的信号,但是没有看到哪里 设置了信号处理函数,经过搜索,发现其实就在 ngx_os_init 函数里,它调用了 ngx_posix_init

ngx_int_t ngx_posix_init(ngx_log_t *log)
{
    ngx_signal_t      *sig;
    struct sigaction   sa;

    ngx_pagesize = getpagesize();

    if (ngx_ncpu == 0) {
        ngx_ncpu = 1;
    }

    // 在这里初始化信号处理函数,看代码,统一都是 ngx_signal_handler
    for (sig = signals; sig->signo != 0; sig++) {
        ngx_memzero(&sa, sizeof(struct sigaction));
        sa.sa_handler = sig->handler;
        sigemptyset(&sa.sa_mask);
        if (sigaction(sig->signo, &sa, NULL) == -1) {
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                          "sigaction(%s) failed", sig->signame);
            return NGX_ERROR;
        }
    }

    // ...
}

秘密就藏在 sigaction(sig->signo, &sa, NULL) 函数调用里,sa.sa_handler 赋值于 sig->handlersig 来自于迭代 signals

ngx_signal_t  signals[] = {
    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
      ngx_signal_handler },

    { ngx_signal_value(NGX_REOPEN_SIGNAL),
      "SIG" ngx_value(NGX_REOPEN_SIGNAL),
      ngx_signal_handler },

    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
      "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
      ngx_signal_handler },

    { ngx_signal_value(NGX_TERMINATE_SIGNAL),
      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
      ngx_signal_handler },

    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
      "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
      ngx_signal_handler },

    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
      ngx_signal_handler },

    { SIGALRM, "SIGALRM", ngx_signal_handler },

    { SIGINT, "SIGINT", ngx_signal_handler },

    { SIGIO, "SIGIO", ngx_signal_handler },

    { SIGCHLD, "SIGCHLD", ngx_signal_handler },

    { SIGPIPE, "SIGPIPE, SIG_IGN", SIG_IGN },

    { 0, NULL, NULL }
};

可以看到,基本所有的信号处理函数,都是 ngx_signal_handler

void ngx_signal_handler(int signo)
{
    /*
     * 收到对应信号,就设置对应的变量,比如 ngx_reconfigure。它是一个atomic值,
     * 然后 master 进程就会从 sigsuspend 苏醒,然后检测变量进行处理
     */
    char            *action;
    struct timeval   tv;
    ngx_int_t        ignore;
    ngx_err_t        err;
    ngx_signal_t    *sig;

    ignore = 0;

    err = ngx_errno;

    for (sig = signals; sig->signo != 0; sig++) {
        if (sig->signo == signo) {
            break;
        }
    }

    ngx_gettimeofday(&tv);
    ngx_time_update(tv.tv_sec);

    action = "";

    switch (ngx_process) {

    case NGX_PROCESS_MASTER:
    case NGX_PROCESS_SINGLE:
        switch (signo) {

        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
            ngx_quit = 1;
            action = ", shutting down";
            break;

        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        case SIGINT:
            ngx_terminate = 1;
            action = ", exiting";
            break;

        // ...
        }
    }
}

至此,我们大概就知道了 master 是如何工作的,它本身接收一些信号,然后通过比较信号值,改变一些内部的全局 atomic 变量, 之后在循环中,通过判断这些 atomic 变量,来做出对应的行为。

worker 是如何工作的

要看 worker,那我们就得回到 ngx_start_worker_processes

static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n,
                                       ngx_int_t type)
{
    ngx_int_t         i;
    ngx_channel_t     ch;
    struct itimerval  itv;

    ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "start worker processes");

    ch.command = NGX_CMD_OPEN_CHANNEL;

    while (n--) {
        // 起n个worker进程,执行 ngx_worker_process_cycle
        ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
                          "worker process", type);

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

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

            if (i == ngx_process_slot
                || ngx_processes[i].pid == -1
                || ngx_processes[i].channel[0] == -1)
            {
                continue;
            }

            ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                           "pass channel s:%d pid:" PID_T_FMT
                           " fd:%d to s:%d pid:" PID_T_FMT " fd:%d",
                           ch.slot, ch.pid, ch.fd,
                           i, ngx_processes[i].pid,
                           ngx_processes[i].channel[0]);

            /* TODO: NGX_AGAIN */

            ngx_write_channel(ngx_processes[i].channel[0],
                              &ch, sizeof(ngx_channel_t), cycle->log);
        }
    }

    /*
     * we have to limit the maximum life time of the worker processes
     * by 10 days because our millisecond event timer is limited
     * by 24 days on 32-bit platforms
     */

    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 10 * 24 * 60 * 60;
    itv.it_value.tv_usec = 0;

    if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "setitimer() failed");
    }
}

这个函数里,有两个很重要的东西,第一个是 ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL, "worker process", type), 第二个是 ngx_write_channel(ngx_processes[i].channel[0], &ch, sizeof(ngx_channel_t), cycle->log);。我们先来跟踪第一个:

ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,
                            ngx_spawn_proc_pt proc, void *data,
                            char *name, ngx_int_t respawn)
{
    // ...
    pid = fork();

    switch (pid) {

    case -1:
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "fork() failed while spawning \"%s\"", name);
        ngx_close_channel(ngx_processes[s].channel, cycle->log);
        return NGX_ERROR;

    case 0:
        ngx_pid = ngx_getpid();
        proc(cycle, data); // 看起来这里最终也是要返回然后设置下面的这些东西。此处的 proc 是 ngx_worker_process_cycle
        break;

    default:
        break;
    }
}

这里的 proc 就是调用 ngx_spawn_process 时传入的 ngx_worker_process_cycle

static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
    // ...

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->init_process) {
            // 这里调用 init_process,此处就会初始化事件模块等
            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
                /* fatal */
                exit(2);
            }
        }
    }

    // ...

    for ( ;; ) {
        // worker 真正执行处理事件的地方
        ngx_process_events(cycle);

        // ...

        if (ngx_terminate) {
            // ...
        }

        if (ngx_quit) {
            // ...
        }

        if (ngx_reopen) {
            // ...
        }

        // ...
    }
}

这就是 worker 大概的启动流程,由 master fork之后,初始化各个模块,然后进入自身的工作循环,执行 ngx_process_events 处理各种事件,ngx_process_events 的定义为:

// src/event/ngx_event.h 里
#define ngx_process_events   ngx_event_actions.process_events

// src/event/ngx_event.c 里
ngx_event_actions_t               ngx_event_actions;

// src/event/ngx_event.h 里
typedef struct {
    // 添加/删除事件
    ngx_int_t  (*add)(ngx_event_t *ev, int event, u_int flags);
    ngx_int_t  (*del)(ngx_event_t *ev, int event, u_int flags);

    ngx_int_t  (*enable)(ngx_event_t *ev, int event, u_int flags);
    ngx_int_t  (*disable)(ngx_event_t *ev, int event, u_int flags);

    // 添加和删除连接,也就是对读和写都进行处理
    ngx_int_t  (*add_conn)(ngx_connection_t *c);
    ngx_int_t  (*del_conn)(ngx_connection_t *c, u_int flags);

    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t try);
    // 工作循环中处理事件
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle);

    ngx_int_t  (*init)(ngx_cycle_t *cycle);
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;


extern ngx_event_actions_t   ngx_event_actions;

可以看到,ngx_event_actions 就是事件模块,而其中的 process_events 就是处理事件的函数,而且 ngx_event_actions 是 一个全局变量。试着想想,Nginx要支持跨平台,而且多个 I/O 多路复用库,这里又是一个全局变量,我怀疑是编译的时候生成,然后 具体初始化某个 I/O 多路复用 的时候赋值的,搜索一下代码:

$ ag ngx_event_actions
src/event/ngx_event.c
59:ngx_event_actions_t               ngx_event_actions;

src/event/ngx_event.h
219:} ngx_event_actions_t;
222:extern ngx_event_actions_t   ngx_event_actions;
386:#define ngx_process_changes  ngx_event_actions.process_changes
387:#define ngx_process_events   ngx_event_actions.process_events
388:#define ngx_done_events      ngx_event_actions.done
390:#define ngx_add_event        ngx_event_actions.add
391:#define ngx_del_event        ngx_event_actions.del
392:#define ngx_add_conn         ngx_event_actions.add_conn
393:#define ngx_del_conn         ngx_event_actions.del_conn
435:    ngx_event_actions_t     actions;

src/event/modules/ngx_aio_module.c
78:    ngx_event_actions = ngx_aio_module_ctx.actions;

src/event/modules/ngx_epoll_module.c
169:    ngx_event_actions = ngx_epoll_module_ctx.actions;

src/event/modules/ngx_kqueue_module.c
189:    ngx_event_actions = ngx_kqueue_module_ctx.actions;

src/event/modules/ngx_iocp_module.c
112:    ngx_event_actions = ngx_iocp_module_ctx.actions;

src/event/modules/ngx_poll_module.c
101:    ngx_event_actions = ngx_poll_module_ctx.actions;

src/event/modules/ngx_select_module.c
113:    ngx_event_actions = ngx_select_module_ctx.actions;

src/event/modules/ngx_rtsig_module.c
163:    ngx_event_actions = ngx_rtsig_module_ctx.actions;
529:        ngx_event_actions.process_events = ngx_rtsig_process_overflow;
765:    ngx_event_actions.process_events = ngx_rtsig_process_events;

src/event/modules/ngx_devpoll_module.c
168:    ngx_event_actions = ngx_devpoll_module_ctx.actions;

可以看到,代码上确实是在多个 I/O 多路复用的模块里分别赋值的,我的系统是Linux,所以肯定是用 epoll,跳到 ngx_epoll_module.c 看一下:

static int ngx_epoll_init(ngx_cycle_t *cycle)
{
    // ...
    ngx_event_actions = ngx_epoll_module_ctx.actions;
    // ...
}

好,就此打住。我们大概知道了worker的流程,首先由 master fork,然后初始化各个模块,这里就包括了 I/O 多路复用模块, 然后进入自身的工作循环,执行 ngx_process_events 处理各种事件,ngx_process_events 就是由 I/O 多路复用模块提供的。

每次执行完之后,也是通过判断几个全局变量来决定worker的行为。

master/worker 通信

刚才我们说到 ngx_write_channel(ngx_processes[i].channel[0], &ch, sizeof(ngx_channel_t), cycle->log),我们现在来看看它是 干啥的:

// master/worker 之间进程间通信
ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
                            ngx_log_t *log) 
{
    ssize_t             n;
    ngx_err_t           err;
    struct iovec        iov[1];
    struct msghdr       msg;

#if (HAVE_MSGHDR_MSG_CONTROL)

    union {
        struct cmsghdr  cm;
        char            space[CMSG_SPACE(sizeof(int))];
    } cmsg;

    if (ch->fd == -1) {
        msg.msg_control = NULL;
        msg.msg_controllen = 0;

    } else {
        msg.msg_control = (caddr_t) &cmsg;
        msg.msg_controllen = sizeof(cmsg);

        cmsg.cm.cmsg_len = sizeof(cmsg);
        cmsg.cm.cmsg_level = SOL_SOCKET; 
        cmsg.cm.cmsg_type = SCM_RIGHTS;
        *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;
    }

#else

    if (ch->fd == -1) {
        msg.msg_accrights = NULL;
        msg.msg_accrightslen = 0;

    } else {
        msg.msg_accrights = (caddr_t) &ch->fd;
        msg.msg_accrightslen = sizeof(int);
    }

#endif

    iov[0].iov_base = (char *) ch;
    iov[0].iov_len = size;

    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    n = sendmsg(s, &msg, 0);

    if (n == -1) {
        err = ngx_errno;
        if (err == NGX_EAGAIN) {
            return NGX_AGAIN;
        }

        ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed");
        return NGX_ERROR;
    }

    return NGX_OK;
}

这里其实是 master 和 worker 之间通信的代码,也是通过网络,传输的结构体定义为:

typedef struct {
     ngx_uint_t  command;
     ngx_pid_t   pid;
     ngx_int_t   slot;
     ngx_fd_t    fd;
} ngx_channel_t;

可以看到,ch 其实是一个写往通道里的命令。而 channel 的定义则是 ngx_processes[i].channel[0], 其实它是一个 socket 的 fd。那么这个 ngx_processes 是什么时候初始化的呢? 答案就在 ngx_spawn_process 中:

ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,
                            ngx_spawn_proc_pt proc, void *data,
                            char *name, ngx_int_t respawn)
{
    u_long     on;
    ngx_pid_t  pid;
    ngx_int_t  s;

    if (respawn >= 0) {
        s = respawn;

    } else {
        for (s = 0; s < ngx_last_process; s++) {
            if (ngx_processes[s].pid == -1) {
                break;
            }
        }

        if (s == NGX_MAX_PROCESSES) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "no more than %d processes can be spawned",
                          NGX_MAX_PROCESSES);
            return NGX_ERROR;
        }
    }
    // ...
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
    // ...
    pid = fork();
    // ...
}

在 fork 之前,Nginx会在 ngx_processes 里找到一个最小的位置,然后用 socketpair 创建一个 socket pair 用于 master 和 worker 通信。

看到这里,这第一篇终于可以收尾了,在这一篇源码阅读中,我们从 main 函数入手,依次跟踪了 main 函数的启动过程, 然后我们抵达了 ngx_master_process_cycle,从这里开始,Nginx便会进入 master/worker 模型的代码。接着我们了解了 master 大概的启动流程,worker的启动流程,以及他们之间是如何通信的,以及master和worker大致的工作模型。

接下来的文章,我们将聚焦到 worker ,看 worker 中是如何初始化模块的,以及worker是如何处理请求的。


参考资料:


微信公众号
关注公众号,获得及时更新

MySQL EXPLAIN中的filesort是什么?

数据库索引设计与优化

如何调试?

Docker CE 18.03源码阅读与分析

容器时代的日志处理

Golang和Thrift

折腾Kubernetes

协程(coroutine)简介 - 什么是协程?

goroutine 切换的时候发生了什么?

Prometheus 数据类型

Gin源码阅读与分析

如何面试-作为面试官得到的经验

自己写一个容器

Golang(Go语言)中实现典型的fork调用

软件开发之禅---大事化小,各个击破




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK