zoukankan      html  css  js  c++  java
  • 从 mian 函数开始一步一步分析 nginx 执行流程(四)

    如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件!

    这一节我们分析ngx_worker_process_cycle(),该函数代码比较少,因为它通过调用函数实现功能的,先贴出代码:

    [os/unix/ngx_process_cycle.c

     761 /* worker_process 所执行的程序,nginx 核心所在 */
     762 static void
     763 ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
     764 {
     765     ngx_uint_t         i;
     766     ngx_connection_t  *c;
     767 
     768     ngx_process = NGX_PROCESS_WORKER;
     769 
     770     /* worker 进程初始化 */
     771     ngx_worker_process_init(cycle, 1);
     772 
     773     /* 设置程序名称 */
     774     ngx_setproctitle("worker process");

      1. 768 行设置 ngx_process 的值为 NGX_PROCESS_WORKER, 该值会在信号回调函数中有用ngx_signal_handler()[ngx_process.c],该值表示的是进程模型的类型。

      2. 771 行调用 ngx_worker_process_init() 来先对 worker 进程进行一下初始化,该函数的源码如下:

     888 /* worker_process 初始化,根据配置来设置一些属性 */
     889 static void
     890 ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority)
     891 {
     892     sigset_t          set;
     893     ngx_int_t         n;
     894     ngx_uint_t        i;
     895     struct rlimit     rlmt;
     896     ngx_core_conf_t  *ccf;
     897     ngx_listening_t  *ls;
     898 
     899     if (ngx_set_environment(cycle, NULL) == NULL) {
     900         /* fatal */
     901         exit(2);
     902     }
     903 
     904     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
     905 
     906     if (priority && ccf->priority != 0) {
     907         if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
     908             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     909                           "setpriority(%d) failed", ccf->priority);
     910         }
     911     }
     912 
     913     if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
     914         rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
     915         rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;
     916 
     917         if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
     918             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     919                           "setrlimit(RLIMIT_NOFILE, %i) failed",
     920                           ccf->rlimit_nofile);
     921         }
     922     }
     923 
     924     if (ccf->rlimit_core != NGX_CONF_UNSET) {
     925         rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
     926         rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
     927 
     928         if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
     929             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     930                           "setrlimit(RLIMIT_CORE, %O) failed",
     931                           ccf->rlimit_core);
     932         }
     933     }
     934 
     935 #ifdef RLIMIT_SIGPENDING
     936     if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
     937         rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
     938         rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
     939 
     940         if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {
     941             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     942                           "setrlimit(RLIMIT_SIGPENDING, %i) failed",
     943                           ccf->rlimit_sigpending);
     944         }
     945     }
     946 #endif
     947 
     948     if (geteuid() == 0) {
     949         if (setgid(ccf->group) == -1) {
     950             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
     951                           "setgid(%d) failed", ccf->group);
     952             /* fatal */
     953             exit(2);
     954         }
     955 
     956         if (initgroups(ccf->username, ccf->group) == -1) {
     957             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
     958                           "initgroups(%s, %d) failed",
     959                           ccf->username, ccf->group);
     960         }
     961 
     962         if (setuid(ccf->user) == -1) {
     963             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
     964                           "setuid(%d) failed", ccf->user);
     965             /* fatal */
     966             exit(2);
     967         }
     968     }
     969 
     970 #if (NGX_HAVE_SCHED_SETAFFINITY)
     971 
     972     if (cpu_affinity) {
     973         ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
     974                       "sched_setaffinity(0x%08Xl)", cpu_affinity);
     975 
     976         if (sched_setaffinity(0, sizeof(cpu_affinity),
     977                               (cpu_set_t *) &cpu_affinity)
     978             == -1)
     979         {
     980             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     981                           "sched_setaffinity(0x%08Xl) failed", cpu_affinity);
     982         }
     983     }
     984 
     985 #endif
     986 
     987 #if (NGX_HAVE_PR_SET_DUMPABLE)
     988 
     989     /* allow coredump after setuid() in Linux 2.4.x */
     990 
     991     if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
     992         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     993                       "prctl(PR_SET_DUMPABLE) failed");
     994     }
     995 
     996 #endif
     997 
     998     if (ccf->working_directory.len) {
     999         if (chdir((char *) ccf->working_directory.data) == -1) {
    1000             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
    1001                           "chdir("%s") failed", ccf->working_directory.data);
    1002             /* fatal */
    1003             exit(2);
    1004         }
    1005     }
    1006 
    1007     sigemptyset(&set);
    1008 
    1009     if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {
    1010         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
    1011                       "sigprocmask() failed");
    1012     }
    1013 
    1014     /*
    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      */
    1018     ls = cycle->listening.elts;
    1019     for (i = 0; i < cycle->listening.nelts; i++) {
    1020         ls[i].previous = NULL;
    1021     }
    1022 
    1023     for (i = 0; ngx_modules[i]; i++) {
    1024         if (ngx_modules[i]->init_process) {
    1025             /* 如果有模块设置了 init_process 函数 */
    1026             if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
    1027                 /* fatal */
    1028                 exit(2);
    1029             }
    1030         }
    1031     }
    1032 
    1033     for (n = 0; n < ngx_last_process; n++) {
    1034 
    1035         if (ngx_processes[n].pid == -1) {
    1036             continue;
    1037         }
    1038 
    1039         if (n == ngx_process_slot) {
    1040             continue;
    1041         }
    1042 
    1043         if (ngx_processes[n].channel[1] == -1) { // channel²»ŽæÔÚ£¬Ìø¹ý
    1044             continue;
    1045         }
    1046 
    1047         if (close(ngx_processes[n].channel[1]) == -1) {
    1048             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
    1049                           "close() channel failed");
    1050         }
    1051     }
    1052 
    1053     if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
    1054         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
    1055                       "close() channel failed");
    1056     }
    1057 
    1058 #if 0
    1059     ngx_last_process = 0;
    1060 #endif
    1061     /* 貌似要加入读事件 */
    1062     if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
    1063                               ngx_channel_handler)
    1064         == NGX_ERROR)
    1065     {
    1066         /* fatal */
    1067         exit(2);
    1068     }
    1069 }
    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"。

     776 #if (NGX_THREADS)
     777     {
     778     ngx_int_t         n;
     779     ngx_err_t         err;
     780     ngx_core_conf_t  *ccf;
     781 
     782     /* 核心模块的 */
     783     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
     784 
     785     if (ngx_threads_n) {
     786         if (ngx_init_threads(ngx_threads_n, ccf->thread_stack_size, cycle)
     787             == NGX_ERROR)
     788         {
     789             /* fatal */
     790             exit(2);
     791         }
     792 
     793         err = ngx_thread_key_create(&ngx_core_tls_key);
     794         if (err != 0) {
     795             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
     796                           ngx_thread_key_create_n " failed");
     797             /* fatal */
     798             exit(2);
     799         }
     800 
     801         for (n = 0; n < ngx_threads_n; n++) {
     802 
     803             ngx_threads[n].cv = ngx_cond_init(cycle->log);
     804 
     805             if (ngx_threads[n].cv == NULL) {
     806                 /* fatal */
     807                 exit(2);
     808             }
     809 
     810             if (ngx_create_thread((ngx_tid_t *) &ngx_threads[n].tid,
     811                                   ngx_worker_thread_cycle,
     812                                   (void *) &ngx_threads[n], cycle->log)

      

      4. 776-821 行,目前还不支持线程,所以暂时不分析。

      

     823     /* worker 进程也是一个死循环 */
     824     /* 以下的 ngx_exiting 、ngx_quit、ngx_terminate、ngx_reopen 是一个全局变量,是旗标,它们控制着 worker 进程的行为 */
     825     for ( ;; ) {
     826 
     827         /* 如果 ngx_exiting 为 1,则开始准备关闭 worker 进程*/
     828         if (ngx_exiting) {
     829 
     830             c = cycle->connections;
     831             /* 关闭所有正在处理的连接 */
     832             for (i = 0; i < cycle->connection_n; i++) {
     833 
     834                 /* THREAD: lock */
     835 
     836                 if (c[i].fd != -1 && c[i].idle) {
     837                     c[i].close = 1;
     838                     /* 调用他们对应的关闭连接处理方法 */
     839                     c[i].read->handler(c[i].read);
     840                 }
     841             }
     842 
     843             //
     844             if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
     845             {
     846                 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
     847 
     848                 ngx_worker_process_exit(cycle);
     849             }
     850         }
     851 
     852         ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
     853 
     854         /* 通过 ngx_process_events_and_timers 调到对应的时间控制阻塞点 --- 一切事件的监听从这里开始了  */
     855         /* ngx_process_events_and_timers() 定义在 event/ngx_event.h/c 文件中  */
     856         ngx_process_events_and_timers(cycle);/* 处理事件和定时器 */
     857 
     858         /* 强制关闭进程 */
     859         if (ngx_terminate) {
     860             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
     861 
     862             ngx_worker_process_exit(cycle);
     863         }
     864 
     865         /* 优雅的关闭进程 */
     866         if (ngx_quit) {
     867             ngx_quit = 0;
     868             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
     869                           "gracefully shutting down");
     870             ngx_setproctitle("worker process is shutting down");
     871 
     872             if (!ngx_exiting) {
     873                 //¹Ø±Õsocket£¬È»ºóÉèÖÃÍ˳ö±êÖŸ
     874                 ngx_close_listening_sockets(cycle);
     875                 ngx_exiting = 1;
     876             }
     877         }
     878 
     879         /* 重新打开所有文件 */
     880         if (ngx_reopen) {
     881             ngx_reopen = 0;
     882             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
     883             ngx_reopen_files(cycle, -1);
     884         }
     885     }
     886 }

      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 的精髓!>_<

  • 相关阅读:
    [PY3]——heap模块 和 堆排序
    [PY3]——求TopN/BtmN 和 排序问题的解决
    [转载+补充][PY3]——环境配置(2)——windows下安装pycharm并连接Linux的python环境
    [转载+补充]windows下SVN客户端的安装
    [Visual studio code 常见问题解决] ——中文乱码、
    Smrty模版总结(转)
    cms内容模型标签
    phpcms图文总结(转)
    phpcms总结(转)
    PHP总结
  • 原文地址:https://www.cnblogs.com/zhuwbox/p/3986856.html
Copyright © 2011-2022 走看看