从 mian 函数开始一步一步分析 nginx 执行流程(四)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件!
这一节我们分析ngx_worker_process_cycle(),该函数代码比较少,因为它通过调用函数实现功能的,先贴出代码:
[os/unix/ngx_process_cycle.c
- /* worker_process 所执行的程序,nginx 核心所在 */
- static void
- ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
- {
- ngx_uint_t i;
- ngx_connection_t *c;
- ngx_process = NGX_PROCESS_WORKER;
- /* worker 进程初始化 */
- ngx_worker_process_init(cycle, );
- /* 设置程序名称 */
- ngx_setproctitle("worker process");
1. 768 行设置 ngx_process 的值为 NGX_PROCESS_WORKER, 该值会在信号回调函数中有用ngx_signal_handler()[ngx_process.c],该值表示的是进程模型的类型。
2. 771 行调用 ngx_worker_process_init() 来先对 worker 进程进行一下初始化,该函数的源码如下:
- /* worker_process 初始化,根据配置来设置一些属性 */
- static void
- ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority)
- {
- sigset_t set;
- ngx_int_t n;
- ngx_uint_t i;
- struct rlimit rlmt;
- ngx_core_conf_t *ccf;
- ngx_listening_t *ls;
- if (ngx_set_environment(cycle, NULL) == NULL) {
- /* fatal */
- exit();
- }
- ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
- if (priority && ccf->priority != ) {
- if (setpriority(PRIO_PROCESS, , ccf->priority) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "setpriority(%d) failed", ccf->priority);
- }
- }
- if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
- rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
- rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;
- if (setrlimit(RLIMIT_NOFILE, &rlmt) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "setrlimit(RLIMIT_NOFILE, %i) failed",
- ccf->rlimit_nofile);
- }
- }
- if (ccf->rlimit_core != NGX_CONF_UNSET) {
- rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
- rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
- if (setrlimit(RLIMIT_CORE, &rlmt) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "setrlimit(RLIMIT_CORE, %O) failed",
- ccf->rlimit_core);
- }
- }
- #ifdef RLIMIT_SIGPENDING
- if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
- rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
- rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
- if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "setrlimit(RLIMIT_SIGPENDING, %i) failed",
- ccf->rlimit_sigpending);
- }
- }
- #endif
- if (geteuid() == ) {
- if (setgid(ccf->group) == -) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "setgid(%d) failed", ccf->group);
- /* fatal */
- exit();
- }
- if (initgroups(ccf->username, ccf->group) == -) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "initgroups(%s, %d) failed",
- ccf->username, ccf->group);
- }
- if (setuid(ccf->user) == -) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "setuid(%d) failed", ccf->user);
- /* fatal */
- exit();
- }
- }
- #if (NGX_HAVE_SCHED_SETAFFINITY)
- if (cpu_affinity) {
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, ,
- "sched_setaffinity(0x%08Xl)", cpu_affinity);
- if (sched_setaffinity(, sizeof(cpu_affinity),
- (cpu_set_t *) &cpu_affinity)
- == -)
- {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "sched_setaffinity(0x%08Xl) failed", cpu_affinity);
- }
- }
- #endif
- #if (NGX_HAVE_PR_SET_DUMPABLE)
- /* allow coredump after setuid() in Linux 2.4.x */
- if (prctl(PR_SET_DUMPABLE, , , , ) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "prctl(PR_SET_DUMPABLE) failed");
- }
- #endif
- if (ccf->working_directory.len) {
- if (chdir((char *) ccf->working_directory.data) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "chdir(\"%s\") failed", ccf->working_directory.data);
- /* fatal */
- exit();
- }
- }
- sigemptyset(&set);
- if (sigprocmask(SIG_SETMASK, &set, NULL) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "sigprocmask() failed");
- }
- /*
- 1015 * disable deleting previous events for the listening sockets because
- 1016 * in the worker processes there are no events at all at this point
- 1017 */
- ls = cycle->listening.elts;
- for (i = ; i < cycle->listening.nelts; i++) {
- ls[i].previous = NULL;
- }
- for (i = ; ngx_modules[i]; i++) {
- if (ngx_modules[i]->init_process) {
- /* 如果有模块设置了 init_process 函数 */
- if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
- /* fatal */
- exit();
- }
- }
- }
- for (n = ; n < ngx_last_process; n++) {
- if (ngx_processes[n].pid == -) {
- continue;
- }
- if (n == ngx_process_slot) {
- continue;
- }
- if (ngx_processes[n].channel[] == -) { // channel²»ŽæÔÚ£¬Ìø¹ý
- continue;
- }
- if (close(ngx_processes[n].channel[]) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "close() channel failed");
- }
- }
- if (close(ngx_processes[ngx_process_slot].channel[]) == -) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "close() channel failed");
- }
- #if 0
- ngx_last_process = ;
- #endif
- /* 貌似要加入读事件 */
- if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
- ngx_channel_handler)
- == NGX_ERROR)
- {
- /* fatal */
- exit();
- }
- }
ngx_worker_process_init()
2.1 该函数的代码很长,我将代码折叠起来了!该函数有两个变量 cycle 和 priorty,其中是很多函数调用都要用的全局变量 cycle,另一个是进程优先级标识 priority(只有该标识不为 0,配置中设置进程优先级的参数才起作用。)
2.2 904 行获取与 ngx_core_moudle(核心模块) 有关的配置文件的指针。如果要用到 nginx.conf 配置文件中的配置,事先都需要这一步---用来获取模块相关的配置文件指针
2.3 906-911 行根据标识 priorty 和 配置中的 priority 值,调用 setpriority() 函数设置进程的优先级。
2.4 913-922 行根据配置中设置的 rilimit_nofile 选项值,调用 setrlimit() 函数 + RLIMIT_NOFILE 来设置进程所能打开的最大文件描述符的个数 。
2.5 924-933 行根据配置中设置的 rilimit_core 选项值,调用 setrlimit() 函数 + RLIMIT_CORE 来设置内核转存文件的最大长度。
2.6 936-945 行根据配置中设置的 rilimit_sigpending 选项值,调用 setrlimit() 函数 + RLIMIT_SIGPENDING 来设置用户可拥有的最大挂起信号数。
2.7 948-968 行判断如果执行该进程(worker 进程)是 root 用户的,支持用户在配置文件中配置的 group,username,user 选项值,根据 group 设置用户 ID 所在组 ID 的值、根据 username 和 group 从组文件(/etc/group)中读取一项组数据、根据 user 选项值设置当前进程的有效 ID 和实际 ID 都设置为 user 值。
2.8 972-983 行根据是否设置 cpu_affinity 来是否将进程 attach 到一个指定 CPU 上运行!
2.9 998-1005 行根据是否设置了 worker 进程的工作目录(working_directory)来改变当前进程( worker 进程) 到指定目录!
2.10 1007-1012 行初始化信号有关的变量 set !
2.11 1019-1021 行遍历所有监听套接字结构体链表---待分析,英文解释部分不是很懂/* disable deleting previous events for the listening sockets because in the worker processes there are no events at all at this point */
2.12 1023-1031 行调用每个模块设置的 init_process 回调函数!
2.13 之前我们说过每个 worker 进程在创建的时候都会创建 一对 channel(channel[0]和channel[0]),在进程之间进行通信的时候每个进程只要用到一个就行了 channel[0] 或者 channel[1],1033-1056 行很明显是关闭正在运行的 worker 进程的 channel[0]端(要用 channel[1]端j进行读写),关闭处正在运行的 worker 进程以外的 worker 进程的 channel[1] 。
2.14 1062-1068 行调将 channel 套接口字上的读事件加入监听行列。
3. 774 行设置进程的名称,这里显然设置为 "worker process"。
- #if (NGX_THREADS)
- {
- ngx_int_t n;
- ngx_err_t err;
- ngx_core_conf_t *ccf;
- /* 核心模块的 */
- ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
- if (ngx_threads_n) {
- if (ngx_init_threads(ngx_threads_n, ccf->thread_stack_size, cycle)
- == NGX_ERROR)
- {
- /* fatal */
- exit();
- }
- err = ngx_thread_key_create(&ngx_core_tls_key);
- if (err != ) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
- ngx_thread_key_create_n " failed");
- /* fatal */
- exit();
- }
- for (n = ; n < ngx_threads_n; n++) {
- ngx_threads[n].cv = ngx_cond_init(cycle->log);
- if (ngx_threads[n].cv == NULL) {
- /* fatal */
- exit();
- }
- if (ngx_create_thread((ngx_tid_t *) &ngx_threads[n].tid,
- ngx_worker_thread_cycle,
- (void *) &ngx_threads[n], cycle->log)
4. 776-821 行,目前还不支持线程,所以暂时不分析。
- /* worker 进程也是一个死循环 */
- /* 以下的 ngx_exiting 、ngx_quit、ngx_terminate、ngx_reopen 是一个全局变量,是旗标,它们控制着 worker 进程的行为 */
- for ( ;; ) {
- /* 如果 ngx_exiting 为 1,则开始准备关闭 worker 进程*/
- if (ngx_exiting) {
- c = cycle->connections;
- /* 关闭所有正在处理的连接 */
- for (i = ; i < cycle->connection_n; i++) {
- /* THREAD: lock */
- if (c[i].fd != - && c[i].idle) {
- c[i].close = ;
- /* 调用他们对应的关闭连接处理方法 */
- 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, , "exiting");
- ngx_worker_process_exit(cycle);
- }
- }
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, , "worker cycle");
- /* 通过 ngx_process_events_and_timers 调到对应的时间控制阻塞点 --- 一切事件的监听从这里开始了 */
- /* ngx_process_events_and_timers() 定义在 event/ngx_event.h/c 文件中 */
- ngx_process_events_and_timers(cycle);/* 处理事件和定时器 */
- /* 强制关闭进程 */
- if (ngx_terminate) {
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "exiting");
- ngx_worker_process_exit(cycle);
- }
- /* 优雅的关闭进程 */
- if (ngx_quit) {
- ngx_quit = ;
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, ,
- "gracefully shutting down");
- ngx_setproctitle("worker process is shutting down");
- if (!ngx_exiting) {
- //¹Ø±Õsocket£¬È»ºóÉèÖÃÍ˳ö±êÖŸ
- ngx_close_listening_sockets(cycle);
- ngx_exiting = ;
- }
- }
- /* 重新打开所有文件 */
- if (ngx_reopen) {
- ngx_reopen = ;
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "reopening logs");
- ngx_reopen_files(cycle, -);
- }
- }
- }
5. worker 进程本身也是个死循环。上面的 825-885 行就是 worker 进程所在的循环
6. 之前的代码我们分析过在 master 进程收到一些信号时,有的信号会通过 ngx_signal_worker_processes() 函数发送给 worker 进程,在这个 for 循环中 worker 进程就要处理收到的这些信号。
7. 828-850 行,ngx_exiting 为 1,关闭所有的连接。清除所有定时器然后调用 ngx_worker_process_exit() 退出 worker 进程。
8. 856 行,非常重要的一行,所有的事件都在这个函数处理,服务器就等待在这个函数中,等待事件的到来(新连接到达或者客户端发来数据)! 我们将在下一节分析该函数。
9. 859-863 行根据是否设置 ngx_terminate 标志位来是否强制关闭进程(ngx_worker_process_exit())!
10. 866-877 行根据 ngx_quit 标志外来优雅的关闭进程。为什么说优雅呢?在这段代码中并不直接调用 ngx_worker_process_exit() 立即关闭进程,而是关闭所有监听套接口描述符同时设置 ngx_exiting = 1,也就是说到一个循环才会关闭该进程,让该进程在关闭之前执行完剩下的循环,做一些在关闭前还没有完全做完的工作。所以说是优雅的退出。
11. 880-884行调用 ngx_reopen_files 重新打开 worker 进程所打开的文件。
12. 至此,ngx_worker_process_cycle() 函数分析完毕。接下来要分析的函数是 ngx_process_events_and_timers(),这个函数处理所有事件。是 nginx 的精髓!>_<
从 mian 函数开始一步一步分析 nginx 执行流程(四)的更多相关文章
- 从 mian 函数开始一步一步分析 nginx 执行流程(二)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件! 上一个博客中我们将 main 函数执行流程分析完,到最后一步调用 ngx_master_process ...
- 从 mian 函数开始一步一步分析 nginx 执行流程(三)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件! 这一节我们分析ngx_start_worker_processes(),该函数代码比较少,因为它通过调 ...
- 从 mian 函数开始一步一步分析 nginx 执行流程(一)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件! 我们先贴出 main 函数的部分代码: [core/nginx.c] int ngx_cdecl ma ...
- javascript 函数 add(1)(2)(3)(4)实现无限极累加 —— 一步一步原理解析
问题:我们有一个需求,用js 实现一个无限极累加的函数, 形如 add(1) //=> 1; add(1)(2) //=> 2; add(1)(2)(3) //=> 6; add ...
- 一步一步开发Game服务器(四)地图线程
时隔这么久 才再一次的回归正题继续讲解游戏服务器开发. 开始讲解前有一个问题需要修正.之前讲的线程和定时器线程的时候是分开的. 但是真正地图线程与之前的线程模型是有区别的. 为什么会有区别呢?一个地图 ...
- 一步一步学ROP之linux_x64篇
一步一步学ROP之linux_x64篇 一.序 **ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防 ...
- 一步一步学ROP之linux_x86篇
一步一步学ROP之linux_x86篇 作者:蒸米@阿里聚安全 一.序 ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过 ...
- 大流量网站性能优化:一步一步打造一个适合自己的BigRender插件
BigRender 当一个网站越来越庞大,加载速度越来越慢的时候,开发者们不得不对其进行优化,谁愿意访问一个需要等待 10 秒,20 秒才能出现的网页呢? 常见的也是相对简单易行的一个优化方案是 图片 ...
- 使用Python一步一步地来进行数据分析总结
原文链接:Step by step approach to perform data analysis using Python译文链接:使用Python一步一步地来进行数据分析--By Michae ...
随机推荐
- 以非root权限安装nginx及运行
本章主要讲如何在无root权限(包含无sudo权限)条件下于centos命令行中安装nginx以及在大于1024的端口(这里用8080)上运行. 1. 安装 两种方式,一是下载预编译好的rpm包安装, ...
- python 面向对象简单理解
面向对象: 是一种程序设计范型 作用: 提高软件的重用性和灵活性,扩展性 世界万物一切皆为对象,对象即是指由特定状态,特征,行为的实体 知识点一: 代码的重用 举个栗子 比如小月月有了一个女朋友1 ...
- SQL Server中建立外键的方法
在SQL中建立外键约束,可以级联查询表中的数据,在C#代码生成器中,也能根据外键关系生成相应的外键表数据模型.外键也可防止删除有外键关系的记录,一定程度上保护了数据的安全性. 步骤: 1.要建立外键关 ...
- css3之@font-face---再也不用被迫使用web安全字体了
1,@font-face 的出现在没有css3之前,我们给网页设计字体只能设置web安全字体,使得我们的网页表现看上去好像都是那个样子,那么如果我们想给字体设置web设计者提供的字体呢?没有问题,cs ...
- ionic 项目分享No.2——简化版【转】
写在文章前:由于最近研究ionic框架,深感这块的Demo寥寥可数,而大家又都藏私,堂堂天朝,何时才有百家争鸣之象,开源精神吾辈当仁不让! ...
- mybatis中几种typeHandler的定义使用
1.存储到数据库, 将LONG数组转换成字符串;从数据库获取数据, 将字符串转为LONG数组 package com.winturn.utils.handler; import java.sql.Ca ...
- 初学HTML5系列一:简单介绍
最近很闲,就想着学点东西,然后就瞄中了html5,以前只看过很简单的一些,这次是系统的学下,顺便也记录下.废话不多说,开始正题. 稍微介绍下html5,html5是W3C和WHATWG 合作的结果. ...
- MySQL使用指南(上)
作者:大金刚 有很多朋友虽然安装好了mysql但却不知如何使用它.在这篇文章中我们就从连接MYSQL.修改密码.增加用户等方面来学习一些MYSQL的常用命令. 一.连接MYSQL. 格式: mys ...
- IOS多线程知识总结/队列概念/GCD/串行/并行/同步/异步
进程:正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间: 线程:线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程. 队列:dis ...
- 网站访问架构cdn与负载均衡
曾经见到知乎上有人问“为什么像facebook这类的网站需要上千个工程师维护?”,下面的回答多种多样,但总结起来就是:一个高性能的web系统需 要从无数个角度去考虑他,大到服务器的布局,小到软件中某个 ...