热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Nginx】启动过程

从应用程序的启动过程中main功能开始跟踪。解析命令行參数并保存到ngx_cycle_t结构体中,在ngx_process_options函数中将保存配置文件路径。调
从应用程序的启动过程中main功能开始跟踪。

解析命令行參数并保存到ngx_cycle_t结构体中,在ngx_process_options函数中将保存配置文件路径。

调用ngx_add_inherited_sockets函数获取环境变量中关于平滑升级的一些信息。平滑升级时,旧的master进程会通过环境变量发送传递一些信息给新的master进程,新的master进程启动后要去环境变量中取得这些信息,这就是这个函数的作用。

调用ngx_init_cycle初始化ngx_cycle_t结构体,当中包含:
  • 调用每个核心模块的create_conf方法创建存储配置项參数的结构体。

    注意,非核心模块存储配置项的结构体不在这里创建,而是由核心模块负责创建。

  • 调用配置模块提供的解析方法,遍历配置文件。对于每个配置项又遍历全部核心模块,核心模块感兴趣的配置项是存放在ngx_command_t结构体中的。找出对该配置项感兴趣的合兴模块后调用ngx_command_t中定义的解析方法。
  • 调用全部核心模块的init_conf方法,依据解析的配置项參数进行初始化。
  • 创建并打开所需的文件夹和文件。
  • 初始化用于进程间通信的共享内存。
  • 调用ngx_open_listening_sockets函数打开由各Nginx模块从配置文件里读取到的监听port。

    全部的监听port都保存在ngx_cycle_t->listening数组中。存储元素是ngx_listening_t结构体。表示监听port及相关參数。

  • 调用全部模块的init_module方法,这种方法保存在ngx_module_t->init_module函数指针中。
假设配置文件设为单进程工作模式(master_process为off),将调用ngx_single_process_cycle函数。在该函数中调用全部模块的init_process方法,该方法保存在ngx_module_t->init_process函数指针中。系统进入单进程模循环工作。

假设配置文件设为master-worker工作模式(master_process为on),则调用ngx_master_process_cycle函数。该函数fork(封装在ngx_start_worker_processes函数中)出work子进程。work进程会进入ngx_worker_process_cycle函数。该函数首先调用ngx_worker_process_init方法,后者调用全部模块的init_process方法。然后work进程在ngx_worker_process_cycle中循环工作。

再来一张总体流程图:


worker进程工作流程
worker进程在ngx_worker_process_cycle函数的for循环中不断执行。它检查四个标志位:
  • ngx_terminate:表示强制关闭进程。由信号处理函数ngx_signal_handler函数置1.
  • ngx_quit:表示优雅关闭进程,由信号处理函数ngx_signal_handler函数置1.
  • ngx_reopen:表示又一次打开文件,由信号处理函数ngx_signal_handler函数置1.
  • ngx_exiting:表示将要退出进程,仅在为0且ngx_quit被置的情况下置1.
标志位和信号相应关系例如以下图:

worker进程通过检查以上四个标志位来决定程序运行流。我把整个函数做了简化。代码例如以下:

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{ngx_int_t worker = (intptr_t) data;ngx_uint_t i;ngx_connection_t *c;/* 该函数内会调用全部模块的init_process方法 */ngx_worker_process_init(cycle, worker);...for ( ;; ){/* 退出进程标识 */if (ngx_exiting){c = cycle->connections;/* 关闭全部连接 */for (i = 0; i connection_n; i++){if (c[i].fd != -1 && c[i].idle){c[i].close = 1; /* close标识置1 */c[i].read->handler(c[i].read); /* 调用读事件处理方法 */}}if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel){/* 红黑树为空,已经处理全然部事件。调用全部模块的exit_process方法 */ngx_worker_process_exit(cycle);}}/* 还有事件须要处理,继续向下运行 */ngx_process_events_and_timers(cycle); /* 处理事件,事件模块核心方法 *//* 强制关闭进程 */if (ngx_terminate)ngx_worker_process_exit(cycle); /* 直接调用全部模块的exit_process方法退出worker进程 *//* 优雅的关闭连接 */if (ngx_quit){ngx_quit = 0;ngx_setproctitle("worker process is shutting down"); /* 改动进程名字 */if (!ngx_exiting){ngx_close_listening_sockets(cycle); /* 关闭监听句柄 */ngx_exiting = 1; /* ngx_exiting标识唯一被改动的地方 */}}if (ngx_reopen){/* 又一次打开全部文件 */ngx_reopen = 0;ngx_reopen_files(cycle, -1);}}
}


流程图例如以下:



master进程工作流程
master进程在函数ngx_master_process_cycle中不断循环。它不处理网络事件,它仅仅是管理worker子进程。

和worker进程类似。master通过监控七个标志位来决定程序运行流。这七个标志位例如以下所看到的:



下面是经过简化的ngx_master_process_cycle函数:

void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{.../* 依据配置文件启动worker_processes个work子进程 */ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);/* master进程 */for ( ;; ){sigsuspend(&set); /* 堵塞,等待信号 */if (ngx_reap){ngx_reap = 0;/* 监控全部子进程,又一次启动非正常退出的子进程 */live = ngx_reap_children(cycle);}/* live为0表示全部子进程已经退出 */if (!live && (ngx_terminate || ngx_quit)){/* 满足上述条件,退出master进程,包含:* 1、删除存储进程的pid文件* 2、调用全部模块的exit_master方法* 3、关闭监听port* 4、销毁内存池*/ngx_master_process_exit(cycle);}if (ngx_terminate) /* 强制关闭 */{if (delay > 1000){ngx_signal_worker_processes(cycle, SIGKILL); /* SIGKILL = 9 */}else{/* NGX_TERMINATE_SIGNAL = TERM = 15* 向每一个子进程发送TERM信号*/ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_TERMINATE_SIGNAL));}continue; /* 跳上去挂起 */}if (ngx_quit) /* 优雅的退出。向全部子进程发送QUIT信号 */{ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); /* NGX_SHUTDOWN_SIGNAL = QUIT */ls = cycle->listening.elts;/* 关闭全部port */for (n = 0; n listening.nelts; n++)ngx_close_socket(ls[n].fd);cycle->listening.nelts = 0;continue; /* 跳上去挂起 */}if (ngx_reconfigure) /* 须要又一次读取配置文件 */{/* 又一次读取配置文件后,生成新的worker进程,销毁旧的worker进程 */ngx_reconfigure = 0;cycle = ngx_init_cycle(cycle); /* 又一次配置ngx_cycle_t结构体 */ngx_cycle = cycle;ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);/* 又一次派生子进程 */ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN);ngx_start_cache_manager_processes(cycle, 1);live = 1; /* 表示有子进程在执行 *//* 向旧的子进程发送QUIT信号,要求它们退出 */ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));}if (ngx_restart) /* 重新启动work进程 */{ngx_restart = 0;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_reopen_files(cycle, ccf->user);/* 向全部子进程发送USR1信号,要求每一个子进程又一次打开文件 */ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_REOPEN_SIGNAL));}if (ngx_change_binary) /* 平滑升级 */{ngx_change_binary = 0;ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv); /* 新的子进程启动新版本号的Nginx */}if (ngx_noaccept) /* 优雅的关闭服务 */{ngx_noaccept = 0;ngx_noaccepting = 1;/* 向全部子进程发送QUIT信号,要求它们优雅的关闭服务 */ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));}}
}

流程图例如以下:


參考:
《深入理解Nginx》 P275-P286.

版权声明:本文博主原创文章,博客,未经同意不得转载。




推荐阅读
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文详细介绍了git常用命令及其操作方法,包括查看、添加、提交、删除、找回等操作,以及如何重置修改文件、抛弃工作区修改、将工作文件提交到本地暂存区、从版本库中删除文件等。同时还介绍了如何从暂存区恢复到工作文件、恢复最近一次提交过的状态,以及如何合并多个操作等。 ... [详细]
author-avatar
江苏经贸学院
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有