搜尋
首頁運維Nginx講解nginx事件模組的實作細節

在《關於nginx事件模組結構體的詳解》這篇文章中,我們講解nginx的事件模組的整體工作流程,並且著重講解了組織事件模組的各個方法的作用,本文則主要圍繞這整個流程,從原始碼的角度來講解nginx事件模組的實作細節。

講解nginx事件模組的實作細節

1. ngx_events_block()----events配置區塊解析

nginx在解析nginx.conf設定檔時,如果目前解析的組態項目名稱為events,且是一個設定區塊,則會呼叫ngx_events_block()方法解析該配置區塊,如下是該方法的原始碼:

static char * ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
  char *rv;
  void ***ctx;
  ngx_uint_t i;
  ngx_conf_t pcf;
  ngx_event_module_t *m;
  // 如果存储事件模块配置数据的配置项不为空,说明已经解析过配置项了,因而直接返回
  if (*(void **) conf) {
    return "is duplicate";
  }
  // 这里主要是计算event模块的个数,并且将各个event模块的相对顺序标记在了该模块的ctx_index属性中
  ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);
  // 创建一个存储配置项数组的指针
  ctx = ngx_pcalloc(cf->pool, sizeof(void *));
  if (ctx == NULL) {
    return NGX_CONF_ERROR;
  }
  // 为配置项指针申请数组内存
  *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
  if (*ctx == NULL) {
    return NGX_CONF_ERROR;
  }
  // 将数组值赋值到conf中,也即关联到核心配置对象ngx_cycle_t中
  *(void **) conf = ctx;
  for (i = 0; cf->cycle->modules[i]; i++) {
    if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
      continue;
    }
    m = cf->cycle->modules[i]->ctx;
    // 如果当前模块的create_conf()方法不为空,则调用该方法创建存储配置项的结构体
    if (m->create_conf) {
      (*ctx)[cf->cycle->modules[i]->ctx_index] = m->create_conf(cf->cycle);
      if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {
        return NGX_CONF_ERROR;
      }
    }
  }
  // 这里将*cf结构体进行了复制,临时存储在pcf中,然后初始化当前的*cf结构体的模块相关的参数,
  // 以进行下一步的解析
  pcf = *cf;
  cf->ctx = ctx;
  cf->module_type = NGX_EVENT_MODULE;
  cf->cmd_type = NGX_EVENT_CONF;
  // 解析events{}配置块中的子配置项
  rv = ngx_conf_parse(cf, NULL);
  // 重新将pcf复制给*cf,以供后面返回使用
  *cf = pcf;
  if (rv != NGX_CONF_OK) {
    return rv;
  }
  // 到这里,说明events{}配置块的配置项都解析完成了,因而这里调用各个模块的init_conf()方法,
  // 进行配置项的初始化和合并工作
  for (i = 0; cf->cycle->modules[i]; i++) {
    if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
      continue;
    }
    m = cf->cycle->modules[i]->ctx;
    // 如果当前模块的init_conf()不为空,则调用其init_conf()方法初始化配置项
    if (m->init_conf) {
      rv = m->init_conf(cf->cycle, (*ctx)[cf->cycle->modules[i]->ctx_index]);
      if (rv != NGX_CONF_OK) {
        return rv;
      }
    }
  }
  return NGX_CONF_OK;
}

ngx_events_block()方法主要完成的工作有以下幾個:

● 呼叫ngx_count_modules()方法對事件模組序號進行標記,需要注意的是,這裡的排序是針對當前模組在所有事件類型模組中的順序進行標記,並且將序號保存在各模組的ctx_index屬性中,例如這裡的事件類型核心模組ngx_event_core_modulectx_index就為0;

● 為指標ctx申請記憶體空間,並且申請一個數組,將其位址賦值給ctx指針,這裡的數組長度就為事件模組的數目。其實這裡的數組就是用來保存每個事件模組的配置物件的,當前事件模組在所有事件模組中的相對位置就對應於該數組中的相對位置,這裡的相對位置也即前一步中計算得到的ctx_index;

● 呼叫各個事件模組的create_conf()方法建立各自的配置結構體,並且將其保存在ctx#指針指向的陣列中;

● 呼叫ngx_conf_parse()方法對設定檔繼續解析,前面我們已經講到,ngx_events_block()方法就是解析到events配置項的時候才呼叫的,因而這裡的ngx_conf_parse()方法的呼叫就是繼續解析events配置塊的子配置項,而該方法調用完成則說明events配置塊裡的配置項目都已經解析完成;

● 調用各個模組的init_conf()方法對配置項進行初始化,簡單的說,就是,由於在nginx.conf中只配置了部分配置項目的值,而剩餘的配置項就由init_conf()方法來設定預設值;

2. ngx_event_init_conf( )----檢查事件模組配置結構體是否正常建立

在nginx解析完nginx.conf設定檔的所有組態項目後(包括前一步講解的對events配置項的解析),就會呼叫所有核心模組的init_conf()方法對核心模組的配置項進行初始化。這裡的核心模組就包含ngx_events_module,該模組的init_conf()方法指向的就是這裡的ngx_event_init_conf()方法,本質上並沒有做什麼工作,只是檢查了是否創建了儲存事件模組配置項的結構體數組。

如下是ngx_event_init_conf()方法的原始碼:

static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf) {
  if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                  "no \"events\" section in configuration");
    return NGX_CONF_ERROR;
  }
  return NGX_CONF_OK;
}

上面兩個方法就是ngx_events_module核心模組的兩個主要的設定方法,可以看到,這個核心模組的主要作用就是創建了一個數組,用於儲存各個事件模組的配置結構體的。下面我們來看看事件核心模組的主要方法。

3. ngx_event_core_create_conf()----建立事件核心模組配置結構體

在第1點我們講到,解析events配置區塊的子配置項目之前,會呼叫各個事件模組的create_conf()方法來建立其使用的儲存配置資料的結構體,而後呼叫ngx_conf_parse()方法來解析子配置項,接著呼叫各個事件模組的init_conf()方法初始化各個模組配置資料的結構體。

這裡ngx_event_core_module_ctx就是一個事件類型的模組,其create_conf屬性指向的就是ngx_event_core_create_conf()方法,而init_conf屬性屬性指向的就是ngx_event_core_init_conf()方法。

這一節我們先講解ngx_event_core_create_conf()方法的實作原理:

static void *ngx_event_core_create_conf(ngx_cycle_t *cycle) {
  ngx_event_conf_t *ecf;
  ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
  if (ecf == NULL) {
    return NULL;
  }
  ecf->connections = NGX_CONF_UNSET_UINT;
  ecf->use = NGX_CONF_UNSET_UINT;
  ecf->multi_accept = NGX_CONF_UNSET;
  ecf->accept_mutex = NGX_CONF_UNSET;
  ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
  ecf->name = (void *) NGX_CONF_UNSET;
  return ecf;
}

可以看到,這裡的ngx_event_core_create_conf()方法本質上就是建立了一個ngx_event_conf_t結構體,並且將各個屬性都設為未設定狀態。

4. ngx_event_core_init_conf()----初始化配置結構體

前面我們講到,在解析完各個子配置項目之後,nginx會呼叫各個事件模組的init_conf()方法,這裡的核心事件模組就是這個ngx_event_core_init_conf()方法,如下是該方法的原始碼:

static char * ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) {
  ngx_event_conf_t *ecf = conf;
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
  int                  fd;
#endif
  ngx_int_t i;
  ngx_module_t *module;
  ngx_event_module_t *event_module;
  module = NULL;
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
  // 测试是否具有创建epoll句柄的权限
  fd = epoll_create(100);
  if (fd != -1) {
    // 关闭创建的epoll句柄,并且将module指向epoll模块
      (void) close(fd);
      module = &ngx_epoll_module;
  } else if (ngx_errno != NGX_ENOSYS) {
      module = &ngx_epoll_module;
  }
#endif
  // 这里,如果没有前面判断的模块类型,则默认使用事件模块中的第一个模块作为事件处理模型
  if (module == NULL) {
    for (i = 0; cycle->modules[i]; i++) {
      if (cycle->modules[i]->type != NGX_EVENT_MODULE) {
        continue;
      }
      event_module = cycle->modules[i]->ctx;
      if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0) {
        continue;
      }
      module = cycle->modules[i];
      break;
    }
  }
  // 如果此时module还是为NULL,则返回异常
  if (module == NULL) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
    return NGX_CONF_ERROR;
  }
  // 下面的操作主要是判断各个属性是否为初始设置的无效值,如果是,则说明nginx.conf中没有配置
  // 关于该属性的配置项,那么这里就会为该属性设置默认值
  ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
  cycle->connection_n = ecf->connections;
  ngx_conf_init_uint_value(ecf->use, module->ctx_index);
  event_module = module->ctx;
  ngx_conf_init_ptr_value(ecf->name, event_module->name->data);
  ngx_conf_init_value(ecf->multi_accept, 0);
  ngx_conf_init_value(ecf->accept_mutex, 0);
  ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);
  return NGX_CONF_OK;
}

ngx_event_core_init_conf()方法的主要做了两件事:

● 选择当前所使用的模块,如果没指定,则默认使用第一个事件模块;

● 初始化事件核心模块的配置结构体的各个属性值为默认值。

5. ngx_event_module_init()----核心模块的配置项初始化

对于ngx_event_core_module模块而言,其还指定了两个方法,一个是用于初始化模块的ngx_event_module_init()方法,另一个是用于worker进程执行主循环逻辑之前进行调用的ngx_event_process_init()方法。

ngx_event_module_init()方法是在master进程中调用的,其会在解析完nginx.conf文件中的所有配置项之后调用,本质上,该方法的作用就是对当前配置的核心模块(事件模块)进行初始化。

如下是ngx_event_module_init()方法的源码:

/**
 * 当前方法的主要作用是申请一块用于存储统计数据的共享内存,然后设置ngx_accept_mutex_ptr、
 * ngx_connection_counter、ngx_temp_number等变量的地址,如果开启了slab stat,
 * 那么还会设置ngx_stat_accepted、ngx_stat_handled、ngx_stat_requests等的地址,以统计更多的数据
 */
static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle) {
  void ***cf;
  u_char *shared;
  size_t size, cl;
  ngx_shm_t shm;
  ngx_time_t *tp;
  ngx_core_conf_t *ccf;
  ngx_event_conf_t *ecf;
  // 获取core event module的配置结构体
  cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
  ecf = (*cf)[ngx_event_core_module.ctx_index];
  if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                  "using the \"%s\" event method", ecf->name);
  }
  // 获取core module的配置对象
  ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  ngx_timer_resolution = ccf->timer_resolution;
#if !(NGX_WIN32)
  {
    ngx_int_t limit;
    struct rlimit rlmt;
    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "getrlimit(RLIMIT_NOFILE) failed, ignored");
    } else {
      // 这里主要是检查当前事件模块配置的connections数目是否超过了操作系统限制的最大文件句柄数,
      // 或者超过了配置文件中指定的最大文件句柄数
      if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
          && (ccf->rlimit_nofile == NGX_CONF_UNSET
              || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile)) {
        limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
                (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;
        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                      "%ui worker_connections exceed "
                      "open file resource limit: %i",
                      ecf->connections, limit);
      }
    }
  }
#endif /* !(NGX_WIN32) */
  if (ccf->master == 0) {
    return NGX_OK;
  }
  if (ngx_accept_mutex_ptr) {
    return NGX_OK;
  }
  /* cl should be equal to or greater than cache line size */
  cl = 128;
  size = cl            /* ngx_accept_mutex */
         + cl          /* ngx_connection_counter */
         + cl;         /* ngx_temp_number */
#if (NGX_STAT_STUB)
  size += cl           /* ngx_stat_accepted */
         + cl          /* ngx_stat_handled */
         + cl          /* ngx_stat_requests */
         + cl          /* ngx_stat_active */
         + cl          /* ngx_stat_reading */
         + cl          /* ngx_stat_writing */
         + cl;         /* ngx_stat_waiting */
#endif
  // 设置共享内存的大小
  shm.size = size;
  ngx_str_set(&shm.name, "nginx_shared_zone");
  shm.log = cycle->log;
  // 为共享内存结构体申请内存块
  if (ngx_shm_alloc(&shm) != NGX_OK) {
    return NGX_ERROR;
  }
  // addr就是申请的共享内存块的地址
  shared = shm.addr;
  ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
  ngx_accept_mutex.spin = (ngx_uint_t) -1;
  if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared, cycle->lock_file.data) != NGX_OK) {
    return NGX_ERROR;
  }
  // 获取ngx_connection_counter的地址
  ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);
  // 将ngx_connection_counter的值设置为1
  (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
  ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                 "counter: %p, %uA",
                 ngx_connection_counter, *ngx_connection_counter);
  // 获取ngx_temp_number的地址
  ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);
  tp = ngx_timeofday();
  // 生成一个随机数
  ngx_random_number = (tp->msec << 16) + ngx_pid;
#if (NGX_STAT_STUB)
  ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
  ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
  ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
  ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
  ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
  ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
  ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
#endif
  return NGX_OK;
}

ngx_event_module_init()方法主要完成的工作有如下几个:

● 获取配置的timer_resolution属性值,并将其赋值给ngx_timer_resolution属性,这个属性的作用主要是指定更新nginx缓存的时间的定时任务的执行时间间隔;

● 获取nginx配置的文件描述符和当前操作系统的文件描述符的配置,对比两个值,从而更新当前进程所能开启的文件描述符的个数;

● 声明一块共享内存,用于存储nginx进行统计用的各个属性的数据。

6. ngx_event_process_init()----初始化worker进程

ngx_event_process_init()方法主要是在worker进程执行主循环之前进行初始化调用的,如下是该方法的源码:

static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) {
  ngx_uint_t m, i;
  ngx_event_t *rev, *wev;
  ngx_listening_t *ls;
  ngx_connection_t *c, *next, *old;
  ngx_core_conf_t *ccf;
  ngx_event_conf_t *ecf;
  ngx_event_module_t *module;
  // 获取核心模块的配置对象
  ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  // 获取事件核心模块的配置对象
  ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
  // 判断当前如果满足三个条件,则标记当前为使用共享锁的方式:
  // 1. 当前为master-worker模式;
  // 2. 当前worker进程的数量大于1;
  // 3. 当前打开了使用共享锁的开关;
  if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
    ngx_use_accept_mutex = 1;
    ngx_accept_mutex_held = 0;
    ngx_accept_mutex_delay = ecf->accept_mutex_delay;
  } else {
    // 如果不满足上述条件,则指定不使用共享锁
    ngx_use_accept_mutex = 0;
  }
#if (NGX_WIN32)
  /*
   * disable accept mutex on win32 as it may cause deadlock if
   * grabbed by a process which can&#39;t accept connections
   */
  ngx_use_accept_mutex = 0;
#endif
  // 这里这两个队列的主要作用在于,每个worker进程在获取到共享锁之后,就会接收客户端accept事件,
  // 然后将其放入到ngx_posted_accept_events队列中,接着处理该队列中的事件,并且将客户端连接添加到
  // ngx_posted_events队列中,然后再释放锁,也就是说获取锁的worker进程只需要进行accept客户端连接,
  // 然后将锁的权限交给其他的进程,并且再自行处理接收到的连接的读写事件
  // 创建ngx_posted_accept_events队列,该队列用于接收客户端的连接事件
  ngx_queue_init(&ngx_posted_accept_events);
  // 创建ngx_posted_events队列,该队列用于处理客户端连接的读写事件
  ngx_queue_init(&ngx_posted_events);
  // 初始化一个用于存储事件的红黑树
  if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
    return NGX_ERROR;
  }
  for (m = 0; cycle->modules[m]; m++) {
    if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
      continue;
    }
    // ecf->use存储了所选用的事件模型的模块序号,这里是找到该模块
    if (cycle->modules[m]->ctx_index != ecf->use) {
      continue;
    }
    // module即为所选用的事件模型对应的模块
    module = cycle->modules[m]->ctx;
    // 调用指定事件模型的初始化方法
    if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
      /* fatal */
      exit(2);
    }
    break;
  }
#if !(NGX_WIN32)
  // ngx_timer_resolution表示发送更新时间事件的时间间隔
  // 这里表示如果设置了ngx_timer_resolution,并且没有设置定时事件。
  // ngx_event_flags是在事件模块的初始化中设置的,而且只有eventport和kqueue模型才会将
  // NGX_USE_TIMER_EVENT设置到ngx_event_flags中
  if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
    struct sigaction sa;
    struct itimerval itv;
    ngx_memzero(&sa, sizeof(struct sigaction));
    // 这里的sa主要是添加下面的SIGALRM的信号监听事件,该信号的作用是每隔一段时间就会向当前进程发出
    // 当前进程收到信号之后就会调用下面的ngx_timer_signal_handler()方法,该方法中会将
    // ngx_event_timer_alarm设置为1,而后当前进程在进行事件循环的时候,判断如果
    // ngx_event_timer_alarm为1,则会更新当前进程所缓存的时间数据
    sa.sa_handler = ngx_timer_signal_handler;
    sigemptyset(&sa.sa_mask);
    // 添加SIGALRM监听信号
    if (sigaction(SIGALRM, &sa, NULL) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "sigaction(SIGALRM) failed");
      return NGX_ERROR;
    }
    // 设置时间间隔相关参数
    itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
    itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
    itv.it_value.tv_sec = ngx_timer_resolution / 1000;
    itv.it_value.tv_usec = (ngx_timer_resolution % 1000) * 1000;
    // 按照指定的时间间隔设置定时器
    if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "setitimer() failed");
    }
  }
  // NGX_USE_FD_EVENT表示event filter没有透明数据,并需要一个文件描述符表,其主要用于poll、/dev/poll
  if (ngx_event_flags & NGX_USE_FD_EVENT) {
    struct rlimit rlmt;
    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "getrlimit(RLIMIT_NOFILE) failed");
      return NGX_ERROR;
    }
    // 这里主要是初始化最大个数的ngx_connection_t结构体,将其保存在files数组中
    cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;
    cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n, cycle->log);
    if (cycle->files == NULL) {
      return NGX_ERROR;
    }
  }
#else
  if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
      ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                    "the \"timer_resolution\" directive is not supported "
                    "with the configured event method, ignored");
      ngx_timer_resolution = 0;
  }
#endif
  // 申请指定个数的ngx_connection_t数组,这里的connection_n对应的是配置
  // 文件中的worker_connections所指定的大小
  cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
  if (cycle->connections == NULL) {
    return NGX_ERROR;
  }
  c = cycle->connections;
  // 申请指定个数的ngx_event_t数组,其长度与connections数组一致,
  // 这样便可以将connections数组与read_events数组进行对应
  cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
  if (cycle->read_events == NULL) {
    return NGX_ERROR;
  }
  rev = cycle->read_events;
  for (i = 0; i < cycle->connection_n; i++) {
    rev[i].closed = 1;  // 初始状态默认读事件都是closed状态
    rev[i].instance = 1;  // 初始时初始化instance为1
  }
  // 申请指定个数的ngx_event_t数组,其长度与connections数组一致,
  // 这样便可以将connections数组与write_events数组进行对应
  cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
  if (cycle->write_events == NULL) {
    return NGX_ERROR;
  }
  wev = cycle->write_events;
  for (i = 0; i < cycle->connection_n; i++) {
    wev[i].closed = 1;  // 初始时写事件默认也都是closed状态
  }
  i = cycle->connection_n;
  next = NULL;
  do {
    i--;
    // 将read_events和write_events数组的元素依次赋值到connections数组元素的read和write属性中,
    // 并且将connections数组组装成一个单链表
    c[i].data = next;
    c[i].read = &cycle->read_events[i];
    c[i].write = &cycle->write_events[i];
    c[i].fd = (ngx_socket_t) -1;
    next = &c[i];
  } while (i);
  // 初始状态时,所有的connections都未被使用,因而需要存储在free_connections链表中
  cycle->free_connections = next;
  cycle->free_connection_n = cycle->connection_n;
  /* for each listening socket */
  ls = cycle->listening.elts;
  for (i = 0; i < cycle->listening.nelts; i++) {
#if (NGX_HAVE_REUSEPORT)
    if (ls[i].reuseport && ls[i].worker != ngx_worker) {
      continue;
    }
#endif
    // 这里是为当前所监听的每一个端口都绑定一个ngx_connection_t结构体
    c = ngx_get_connection(ls[i].fd, cycle->log);
    if (c == NULL) {
      return NGX_ERROR;
    }
    c->type = ls[i].type;
    c->log = &ls[i].log;
    c->listening = &ls[i];
    ls[i].connection = c;
    rev = c->read;
    rev->log = c->log;
    // 标记accept为1,表示当前可以接收客户端的连接事件
    rev->accept = 1;
#if (NGX_HAVE_DEFERRED_ACCEPT)
    rev->deferred_accept = ls[i].deferred_accept;
#endif
    if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
      if (ls[i].previous) {
        /*
         * delete the old accept events that were bound to
         * the old cycle read events array
         */
        // 删除旧的事件
        old = ls[i].previous->connection;
        if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) {
          return NGX_ERROR;
        }
        old->fd = (ngx_socket_t) -1;
      }
    }
#if (NGX_WIN32)
    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
        ngx_iocp_conf_t  *iocpcf;
        rev->handler = ngx_event_acceptex;
        if (ngx_use_accept_mutex) {
            continue;
        }
        if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
            return NGX_ERROR;
        }
        ls[i].log.handler = ngx_acceptex_log_error;
        iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
        if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
            == NGX_ERROR)
        {
            return NGX_ERROR;
        }
    } else {
        rev->handler = ngx_event_accept;
        if (ngx_use_accept_mutex) {
            continue;
        }
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }
#else
    // SOCK_STREAM表示TCP,一般都是TCP,也就是说在接收到客户端的accept事件之后,
    // 就会调用ngx_event_accept()方法处理该事件
    rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
                                            : ngx_event_recvmsg;
#if (NGX_HAVE_REUSEPORT)
    // 添加当前事件到事件监听队列中
    if (ls[i].reuseport) {
      if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
        return NGX_ERROR;
      }
      continue;
    }
#endif
    if (ngx_use_accept_mutex) {
      continue;
    }
#if (NGX_HAVE_EPOLLEXCLUSIVE)
    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
        && ccf->worker_processes > 1)
    {
        if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
            == NGX_ERROR)
        {
            return NGX_ERROR;
        }
        continue;
    }
#endif
    // 添加当前事件到事件监听队列中
    if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
      return NGX_ERROR;
    }
#endif
  }
  return NGX_OK;
}

这里ngx_event_process_init()方法主要完成了如下几个工作:

● 根据所使用的进程模式和worker进程数量,配置是否使用共享锁的字段ngx_use_accept_mutex

● 初始化用于处理事件的ngx_posted_accept_events队列和ngx_posted_events队列;

● 调用当前所使用的事件模型模块的init()方法,比如epoll模型在该init()方法中就会构建一个epoll句柄,以便后续往其中添加需要监听的事件;

● 判断是否配置了ngx_timer_resolution属性,也即上一节中获取的更新nginx缓存时间的定时任务的执行频率字段,如果配置了,则创建一个定时任务以定时设置ngx_event_timer_alarm属性值;

● 创建长度相同的connectionsread_eventswrite_events数组,并且将connections数组中每个ngx_connection_t结构体的read属性指向read_events数组中对应位置的读事件结构体,将write属性指向write_events数组中对应位置的写事件结构体,并且将所有的connections组织成单链表存储到ngx_cycle_tfree_connections属性中;

● 为当前nginx监听的各个端口配置一个ngx_connection_t结构体,并且为其添加对应的事件监听器,以等待客户端连接的到来。

6. 小结

本文主要讲解了nginx事件模块的初始化方式,以初始化过程中各个方法的调用顺序依次讲解了每个方法的实现原理,以及其在整个流程中所起到的作用。

推荐教程:Nginx教程

以上是講解nginx事件模組的實作細節的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:oschina。如有侵權,請聯絡admin@php.cn刪除
終極攤牌:nginx vs. apache終極攤牌:nginx vs. apacheApr 18, 2025 am 12:02 AM

NGINX適合處理高並發請求,Apache適合需要復雜配置和功能擴展的場景。 1.NGINX採用事件驅動、非阻塞架構,適用於高並發環境。 2.Apache採用進程或線程模型,提供豐富的模塊生態系統,適合複雜配置需求。

nginx行動:示例和現實應用程序nginx行動:示例和現實應用程序Apr 17, 2025 am 12:18 AM

NGINX可用於提升網站性能、安全性和可擴展性。 1)作為反向代理和負載均衡器,NGINX可優化後端服務和分擔流量。 2)通過事件驅動和異步架構,NGINX高效處理高並發連接。 3)配置文件允許靈活定義規則,如靜態文件服務和負載均衡。 4)優化建議包括啟用Gzip壓縮、使用緩存和調整worker進程。

NGINX單元:支持不同的編程語言NGINX單元:支持不同的編程語言Apr 16, 2025 am 12:15 AM

NGINXUnit支持多種編程語言,通過模塊化設計實現。 1.加載語言模塊:根據配置文件加載相應模塊。 2.應用啟動:調用語言運行時執行應用代碼。 3.請求處理:將請求轉發給應用實例。 4.響應返回:將處理後的響應返回給客戶端。

在Nginx和Apache之間進行選擇:適合您的需求在Nginx和Apache之間進行選擇:適合您的需求Apr 15, 2025 am 12:04 AM

NGINX和Apache各有優劣,適合不同場景。 1.NGINX適合高並發和低資源消耗場景。 2.Apache適合需要復雜配置和豐富模塊的場景。通過比較它們的核心特性、性能差異和最佳實踐,可以幫助你選擇最適合需求的服務器軟件。

nginx怎麼啟動nginx怎麼啟動Apr 14, 2025 pm 01:06 PM

問題:如何啟動 Nginx?答案:安裝 Nginx啟動 Nginx驗證 Nginx 是否已啟動探索其他啟動選項自動啟動 Nginx

怎麼查看nginx是否啟動怎麼查看nginx是否啟動Apr 14, 2025 pm 01:03 PM

確認 Nginx 是否啟動的方法:1. 使用命令行:systemctl status nginx(Linux/Unix)、netstat -ano | findstr 80(Windows);2. 檢查端口 80 是否開放;3. 查看系統日誌中 Nginx 啟動消息;4. 使用第三方工具,如 Nagios、Zabbix、Icinga。

nginx怎麼關閉nginx怎麼關閉Apr 14, 2025 pm 01:00 PM

要關閉 Nginx 服務,請按以下步驟操作:確定安裝類型:Red Hat/CentOS(systemctl status nginx)或 Debian/Ubuntu(service nginx status)停止服務:Red Hat/CentOS(systemctl stop nginx)或 Debian/Ubuntu(service nginx stop)禁用自動啟動(可選):Red Hat/CentOS(systemctl disable nginx)或 Debian/Ubuntu(syst

nginx在windows中怎麼配置nginx在windows中怎麼配置Apr 14, 2025 pm 12:57 PM

如何在 Windows 中配置 Nginx?安裝 Nginx 並創建虛擬主機配置。修改主配置文件並包含虛擬主機配置。啟動或重新加載 Nginx。測試配置並查看網站。選擇性啟用 SSL 並配置 SSL 證書。選擇性設置防火牆允許 80 和 443 端口流量。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具