Nginx被称为C++程序员必学的源码之一,我觉得名副其实,它的事件机制、内存管理、进程通信都可以说是顶级实践,非常值得学习。
Nginx源码比较多,本文只看几个重要的模块,更详细的内容请参考《深入理解nginx模块开发与架构》,这本书内容很全,本文只能是跟在后面做一点东施效颦的解读了。
和传统服务器只将事件划分为连接建立和关闭事件不同,Nginx会将事件处理分成很多阶段进行:
划分方法如下:
值得注意的是nginx没有使用多线程,所有的worker和master都是进程实现的。
nginx在进程通信方面真的是教科书级别的案例,所有的进程通信方式都用了个遍。
因为一个进程要处理大量请求,所以nginx的处理方式是尽量不让进程休眠,所以自旋等待用的很多。
对于多线程而言,文件锁和信号量是不推荐使用的:文件锁很容易用错并且需要维护计数结构;信号量没有所有权(不等待信号量的线程也可以解锁信号量)。
nginx更多的也只是将其封装给多进程的ngx_shmtx_t互斥锁使用,实现了阻塞/非阻塞和先自旋再等待的功能而已。所以无论如何,文件锁和信号量能不用就不用。
共享内存这里的操作有三种:使用mmap分配共享内存、以/dev/zero文件使用mmap映射共享内存、用shmget调用来分配共享内存。这三种先定了了哪个就用哪个。
从这里我们也可以看出这三种系统调用的区别:
#if (NGX_HAVE_MAP_ANON)
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
shm->addr = (u_char *) mmap(NULL, shm->size,
PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0);
if (shm->addr == MAP_FAILED) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
return NGX_ERROR;
}
return NGX_OK;
}
void
ngx_shm_free(ngx_shm_t *shm)
{
if (munmap((void *) shm->addr, shm->size) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"munmap(%p, %uz) failed", shm->addr, shm->size);
}
}
#elif (NGX_HAVE_MAP_DEVZERO)
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
ngx_fd_t fd;
fd = open("/dev/zero", O_RDWR);
if (fd == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"open(\"/dev/zero\") failed");
return NGX_ERROR;
}
shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
if (shm->addr == MAP_FAILED) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(/dev/zero, MAP_SHARED, %uz) failed", shm->size);
}
if (close(fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"close(\"/dev/zero\") failed");
}
return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK;
}
void
ngx_shm_free(ngx_shm_t *shm)
{
if (munmap((void *) shm->addr, shm->size) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"munmap(%p, %uz) failed", shm->addr, shm->size);
}
}
#elif (NGX_HAVE_SYSVSHM)
#include
#include
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
int id;
id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));
if (id == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"shmget(%uz) failed", shm->size);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, "shmget id: %d", id);
shm->addr = shmat(id, NULL, 0);
if (shm->addr == (void *) -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");
}
if (shmctl(id, IPC_RMID, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"shmctl(IPC_RMID) failed");
}
return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;
}
void
ngx_shm_free(ngx_shm_t *shm)
{
if (shmdt(shm->addr) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"shmdt(%p) failed", shm->addr);
}
}
#endif
对于x86上的原子操作,nginx使用总线锁来实现。
总线锁(见https://www.zhihu.com/column/p/24146167):
static ngx_inline ngx_atomic_uint_t
ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
ngx_atomic_uint_t set)
{
u_char res;
__asm__ volatile (
// 多核架构下锁总线
NGX_SMP_LOCK
// 将lock和old比较,如果相等则设置lock的值为set
" cmpxchgl %3, %1; "
// 如果相等设置返回值res
" sete %0; "
: "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");
return res;
}
自旋锁的使用场景在于不想陷入睡眠的nginx进程。如果进程陷入睡眠,那么下一次被唤醒的时间是不可控的(或者长到无法接受),因此,自旋等待是更好的方法。
这里的PAUSE是在许多架构体系中专门为了自旋锁而提供的指令,它会告诉CPU现在处于自旋锁等待状态,通常一些CPU会将自己置于节能状态,降低功耗。
实现如下:
void
ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)
{
// lock为0表示锁被释放
// lock不为0表示锁被占用
// value表示希望当锁没有被任何进程持有时(lock为0),将lock设置为value则表示持有了锁
// spin表示在smp架构下,没有拿到锁时,等待其他处理器释放锁的时间
#if (NGX_HAVE_ATOMIC_OPS)
ngx_uint_t i, n;
for ( ;; ) {
if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
return;
}
if (ngx_ncpu > 1) {
for (n = 1; n < spin; n <<= 1) {
for (i = 0; i < n; i++) {
// 调用PAUSE指令 PAUSE指令是REP NOP(重复空操作)指令的一种特殊形式,其操作码为0xF3 0x90
ngx_cpu_pause();
}
if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
return;
}
}
}
ngx_sched_yield();
}
#else
#if (NGX_THREADS)
#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !
#endif
#endif
nginx channel就是常见的socket通信,这里就不说了。
通过sigaction,nginx会注册一组有回调的信号:
ngx_int_t
ngx_init_signals(ngx_log_t *log)
{
ngx_signal_t *sig;
struct sigaction sa;
for (sig = signals; sig->signo != 0; sig++) {
ngx_memzero(&sa, sizeof(struct sigaction));
if (sig->handler) {
sa.sa_sigaction = sig->handler;
sa.sa_flags = SA_SIGINFO;
} else {
sa.sa_handler = SIG_IGN;
}
sigemptyset(&sa.sa_mask);
// 注册信号
if (sigaction(sig->signo, &sa, NULL) == -1) {
#if (NGX_VALGRIND)
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
"sigaction(%s) failed, ignored", sig->signame);
#else
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"sigaction(%s) failed", sig->signame);
return NGX_ERROR;
#endif
}
}
return NGX_OK;
}
信号量的初始化调用int sem_init(sem_t *sem, int pshared, unsigned int value);
其中 pshared标明了这个信号量被进程还是线程使用。
nginx将信号量当作互斥锁使用。
信号量是如何实现互斥锁功能的呢?例如,最初的信号量sem值为0,调用sem_post方法 将会把sem值加1,这个操作不会有任何阻塞;调用sem_wait方法将会把信号量sem的值减1, 如果sem值已经小于或等于0了,则阻塞住当前进程(进程会进入睡眠状态),直到其他进程 将信号量sem的值改变为正数后,这时才能继续通过将sem减1而使得当前进程继续向下执 行。因此,sem_post方法可以实现解锁的功能,而sem_wait方法可以实现加锁的功能。
文件锁可以锁住部分文件区域(fcntl)或者整个文件(flock)。
fcntl和flock的区别如下:
对于文件锁,Nginx封装了3个方法:
为了防止使用操作系统的信号量和互斥锁时导致当前进程被饿死(单线程架构),所以nginx实现了ngx_shmtx_t,用于多进程之间的同步。
这里使用原子操作、信号量、文件锁配合的方式实现:
typedef struct {
#if (NGX_HAVE_ATOMIC_OPS)
// 支持原子操作,使用原子变量
ngx_atomic_t *lock;
#if (NGX_HAVE_POSIX_SEM)
// 支持原子操作和信号量
// 如果支持信号量,那么在原子自旋等待失败后睡眠
ngx_atomic_t *wait;
ngx_uint_t semaphore;
sem_t sem;
#endif
#else
// 不支持原子操作,使用文件锁
ngx_fd_t fd;
u_char *name;
#endif
ngx_uint_t spin;
} ngx_shmtx_t;
如果ngx_shmtx_lock方法在运行一段 时间后,如果其他进程始终不放弃锁,那么当前进程将有可能强制性地获得到这把锁。我们看这里的代码。
ngx_int_t
ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name)
{
if (mtx->name) {
if (ngx_strcmp(name, mtx->name) == 0) {
mtx->name = name;
return NGX_OK;
}
// name与 mtx->name不一致,销毁之前的文件锁
ngx_shmtx_destroy(mtx);
}
mtx->fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,
NGX_FILE_DEFAULT_ACCESS);
if (mtx->fd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,
ngx_open_file_n " \"%s\" failed", name);
return NGX_ERROR;
}
// 只需要inode信息,所以调用unlink删除文件
if (ngx_delete_file(name) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
ngx_delete_file_n " \"%s\" failed", name);
}
mtx->name = name;
return NGX_OK;
}
void
ngx_shmtx_destroy(ngx_shmtx_t *mtx)
{
if (ngx_close_file(mtx->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", mtx->name);
}
}
ngx_uint_t
ngx_shmtx_trylock(ngx_shmtx_t *mtx)
{
ngx_err_t err;
err = ngx_trylock_fd(mtx->fd);
if (err == 0) {
return 1;
}
if (err == NGX_EAGAIN) {
return 0;
}
#if __osf__ /* Tru64 UNIX */
if (err == NGX_EACCES) {
return 0;
}
#endif
ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name);
return 0;
}
void
ngx_shmtx_lock(ngx_shmtx_t *mtx)
{
ngx_err_t err;
err = ngx_lock_fd(mtx->fd);
if (err == 0) {
return;
}
ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name);
}
void
ngx_shmtx_unlock(ngx_shmtx_t *mtx)
{
ngx_err_t err;
err = ngx_unlock_fd(mtx->fd);
if (err == 0) {
return;
}
ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name);
}
ngx_uint_t
ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid)
{
return 0;
}
#endif
#if (NGX_HAVE_ATOMIC_OPS)
static void ngx_shmtx_wakeup(ngx_shmtx_t *mtx);
ngx_int_t
ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name)
{
mtx->lock = &addr->lock;
if (mtx->spin == (ngx_uint_t) -1) {
// 不能使用信号量,返回成功
return NGX_OK;
}
mtx->spin = 2048;
#if (NGX_HAVE_POSIX_SEM)
mtx->wait = &addr->wait;
if (sem_init(&mtx->sem, 1, 0) == -1) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
"sem_init() failed");
} else {
mtx->semaphore = 1;
}
#endif
return NGX_OK;
}
void
ngx_shmtx_destroy(ngx_shmtx_t *mtx)
{
#if (NGX_HAVE_POSIX_SEM)
if (mtx->semaphore) {
if (sem_destroy(&mtx->sem) == -1) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
"sem_destroy() failed");
}
}
#endif
}
ngx_uint_t
ngx_shmtx_trylock(ngx_shmtx_t *mtx)
{
// 这里操作并非原子,ngx_atomic_cmp_set会保证*mtx->lock == 0
return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid));
}
void
ngx_shmtx_lock(ngx_shmtx_t *mtx)
{
ngx_uint_t i, n;
ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock");
for ( ;; ) {
if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {
return;
}
// 多处理器时,等待才有意义
if (ngx_ncpu > 1) {
for (n = 1; n < mtx->spin; n <<= 1) {
for (i = 0; i < n; i++) {
ngx_cpu_pause();
}
if (*mtx->lock == 0
&& ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid))
{
return;
}
}
}
#if (NGX_HAVE_POSIX_SEM)
if (mtx->semaphore) {
(void) ngx_atomic_fetch_add(mtx->wait, 1);
if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {
(void) ngx_atomic_fetch_add(mtx->wait, -1);
return;
}
ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
"shmtx wait %uA", *mtx->wait);
while (sem_wait(&mtx->sem) == -1) {
ngx_err_t err;
err = ngx_errno;
if (err != NGX_EINTR) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
"sem_wait() failed while waiting on shmtx");
break;
}
}
ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
"shmtx awoke");
continue;
}
#endif
ngx_sched_yield();
}
}
void
ngx_shmtx_unlock(ngx_shmtx_t *mtx)
{
if (mtx->spin != (ngx_uint_t) -1) {
ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock");
}
if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) {
ngx_shmtx_wakeup(mtx);
}
}
ngx_uint_t
ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid)
{
ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
"shmtx forced unlock");
if (ngx_atomic_cmp_set(mtx->lock, pid, 0)) {
ngx_shmtx_wakeup(mtx);
return 1;
}
return 0;
}
static void
ngx_shmtx_wakeup(ngx_shmtx_t *mtx)
{
#if (NGX_HAVE_POSIX_SEM)
ngx_atomic_uint_t wait;
if (!mtx->semaphore) {
return;
}
for ( ;; ) {
wait = *mtx->wait;
if ((ngx_atomic_int_t) wait <= 0) {
return;
}
if (ngx_atomic_cmp_set(mtx->wait, wait, wait - 1)) {
break;
}
}
ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
"shmtx wake %uA", wait);
if (sem_post(&mtx->sem) == -1) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
"sem_post() failed while wake shmtx");
}
#endif
}
nginx内存池的设计很特殊,它的生命周期跟随请求或者连接。
这是因为连接往往是短暂的,因此内存池带来了分配内存的便利,又不会因为生命周期过长带来内存泄漏。
它的构成如下:
typedef struct {
// 可分配的位置
u_char *last;
// 当前内存池的结尾
u_char *end;
// 下一个小块内存池
ngx_pool_t *next;
// 分配失败的次数,如果超过 4 ngx_pool_s 中的cunrrent指向next
ngx_uint_t failed;
} ngx_pool_data_t;
struct ngx_pool_s {
// 描述小块内存池
ngx_pool_data_t d;
// 大内存池和小内存池的标准
size_t max;
// 指向第一个小块内存池
ngx_pool_t *current;
ngx_chain_t *chain;
// 大内存池链表
ngx_pool_large_t *large;
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
};
流程如下:
这里很明显有一些内存浪费。
小块内存的大小默认(也是最大大小)是 (ngx_pagesize - 1) 也就是4095,主要是为了防止内存碎片过大。
void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{
char *title;
u_char *p;
size_t size;
ngx_int_t i;
ngx_uint_t sigio;
sigset_t set;
struct itimerval itv;
ngx_uint_t live;
ngx_msec_t delay;
ngx_core_conf_t *ccf;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGIO);
sigaddset(&set, SIGINT);
sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
sigemptyset(&set);
size = sizeof(master_process);
for (i = 0; i < ngx_argc; i++) {
size += ngx_strlen(ngx_argv[i]) + 1;
}
title = ngx_pnalloc(cycle->pool, size);
if (title == NULL) {
/* fatal */
exit(2);
}
p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
for (i = 0; i < ngx_argc; i++) {
*p++ = ' ';
p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
}
ngx_setproctitle(title);
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
ngx_new_binary = 0;
delay = 0;
sigio = 0;
live = 1;
for ( ;; ) {
if (delay) {
if (ngx_sigalrm) {
sigio = 0;
delay *= 2;
ngx_sigalrm = 0;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"termination cycle: %M", delay);
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 0;
itv.it_value.tv_sec = delay / 1000;
itv.it_value.tv_usec = (delay % 1000 ) * 1000;
if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setitimer() failed");
}
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
sigsuspend(&set);
ngx_time_update();
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"wake up, sigio %i", sigio);
if (ngx_reap) {
// 需要监控所有子进程
ngx_reap = 0;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
// 管理子进程
live = ngx_reap_children(cycle);
}
// 所有子进程都退出或者程序终止
if (!live && (ngx_terminate || ngx_quit)) {
ngx_master_process_exit(cycle);
}
// 程序终止但还有子进程没退出
if (ngx_terminate) {
if (delay == 0) {
delay = 50;
}
if (sigio) {
sigio--;
continue;
}
sigio = ccf->worker_processes + 2 /* cache processes */;
// 等待,然后发送kill信号
if (delay > 1000) {
ngx_signal_worker_processes(cycle, SIGKILL);
} else {
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_TERMINATE_SIGNAL));
}
continue;
}
if (ngx_quit) {
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
ngx_close_listening_sockets(cycle);
continue;
}
if (ngx_reconfigure) {
// 重新初始化
ngx_reconfigure = 0;
if (ngx_new_binary) {
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
ngx_noaccepting = 0;
continue;
}
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
cycle = ngx_init_cycle(cycle);
if (cycle == NULL) {
cycle = (ngx_cycle_t *) ngx_cycle;
continue;
}
ngx_cycle = 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_JUST_RESPAWN);
ngx_start_cache_manager_processes(cycle, 1);
/* allow new processes to start */
// 挂起一段时间
ngx_msleep(100);
live = 1;
// 结束旧worker
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
if (ngx_restart) {
ngx_restart = 0;
// 从旧的worker初始化新的worker
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
live = 1;
}
if (ngx_reopen) {
ngx_reopen = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
ngx_reopen_files(cycle, ccf->user);
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_REOPEN_SIGNAL));
}
if (ngx_change_binary) {
ngx_change_binary = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
}
if (ngx_noaccept) {
ngx_noaccept = 0;
ngx_noaccepting = 1;
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
}
}
epoll可能在处理事件的过程中有事件过期了,不能简单的把事件的文件描述符置为-1, 因为中间可能有事件重新打开这个文件描述符,这就导致新的描述符处理了旧的事件。
nginx通过添加标识位instance解决了这个问题。
nginx通过维护一个时间变量表示当前时间,如果设置了timer_resolution,那么就会通过settime系统调用定时更新resolution标识位,在ngx_epoll_process_events时更新缓存时间。
nginx在共享内存之上引入了slab内存来作为多进程复杂数据结构共享的抽象。
它的内存管理如下: