Nginx学习(16)—worker进程循环工作

worker进程循环工作

Nginx采用信号的IPC方式对worker进程进行控制,其中的ngx_terminate、ngx_quit、ngx_repon都将由在ngx_signal_handler方法根据收到的信号进行处理。而信号的初始化都是在main()中的ngx_init_signals()中完成的。下面看源码吧:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static void  
  2. ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)  
  3. {  
  4.     ngx_int_t worker = (intptr_t) data;  
  5.   
  6.     ngx_uint_t         i;  
  7.     ngx_connection_t  *c;  
  8.   
  9.     /* 从master进程继承过来的全局变量,设置其为worker进程 */  
  10.     ngx_process = NGX_PROCESS_WORKER;  
  11.   
  12.     /* Worker进程初始化,下面详述 */  
  13.     ngx_worker_process_init(cycle, worker);  
  14.   
  15.     /* 更改进程名字,这个很有意思...详见《笔记十五》 */  
  16.     ngx_setproctitle("worker process");  
  17.   
  18. /* 针对worker子进程中的线程机制的,一般用不到,这里通过设置NGX_THREADS宏来开启 */  
  19. #if (NGX_THREADS)  
  20.     {  
  21.     ngx_int_t         n;  
  22.     ngx_err_t         err;  
  23.     ngx_core_conf_t  *ccf;  
  24.   
  25.     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  26.   
  27.     if (ngx_threads_n) {  
  28.         if (ngx_init_threads(ngx_threads_n, ccf->thread_stack_size, cycle)  
  29.             == NGX_ERROR)  
  30.         {  
  31.             /* fatal */  
  32.             exit(2);  
  33.         }  
  34.   
  35.         err = ngx_thread_key_create(&ngx_core_tls_key);  
  36.         if (err != 0) {  
  37.             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,  
  38.                           ngx_thread_key_create_n " failed");  
  39.             /* fatal */  
  40.             exit(2);  
  41.         }  
  42.   
  43.         for (n = 0; n < ngx_threads_n; n++) {  
  44.   
  45.             ngx_threads[n].cv = ngx_cond_init(cycle->log);  
  46.   
  47.             if (ngx_threads[n].cv == NULL) {  
  48.                 /* fatal */  
  49.                 exit(2);  
  50.             }  
  51.   
  52.             if (ngx_create_thread((ngx_tid_t *) &ngx_threads[n].tid,  
  53.                                   ngx_worker_thread_cycle,  
  54.                                   (void *) &ngx_threads[n], cycle->log)  
  55.                 != 0)  
  56.             {  
  57.                 /* fatal */  
  58.                 exit(2);  
  59.             }  
  60.         }  
  61.     }  
  62.     }  
  63. #endif  
  64.   
  65.     /* 这里Nginx是通过采用信号的方式,控制worker进程运行,具体的处理信号的方法 
  66.      * 其实在main()函数中的ngx_init_signals()初始化工作中已经弄好,可以见signals[]数组中的 
  67.      * ngx_signal_handler()处理函数,之后打算好好看看ngx_init_signals()这个函数 
  68.      */  
  69.   
  70.     for ( ;; ) {  
  71.   
  72.         /* ngx_exiting将在worker进程收到SIGQUIT信号后设置,具体设置就在下面的ngx_quit语句中 */  
  73.         if (ngx_exiting) {  
  74.   
  75.             c = cycle->connections;  
  76.   
  77.             /* 这是在worker进程收到quit信号退出前,必须将connections上读到的事件处理完 */  
  78.             for (i = 0; i < cycle->connection_n; i++) {  
  79.   
  80.                 /* THREAD: lock */  
  81.   
  82.                 if (c[i].fd != -1 && c[i].idle) {  
  83.                     c[i].close = 1;  
  84.                     c[i].read->handler(c[i].read);  
  85.                 }  
  86.             }  
  87.   
  88.             /* 这里就可以调用ngx_worker_process_exit退出了,这个if条件用到红黑树。。怎么用到的,以后在细究 */  
  89.             if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)  
  90.             {  
  91.                 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");  
  92.   
  93.                 ngx_worker_process_exit(cycle);  
  94.             }  
  95.         }  
  96.   
  97.         ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");  
  98.   
  99.         /* 这个就是worker进程具体的时间和定时器核心处理函数!!!以后慢慢看! */  
  100.         ngx_process_events_and_timers(cycle);  
  101.   
  102.         /* worker进程收到了SIGINT信号,直接强制退出 */  
  103.         if (ngx_terminate) {  
  104.             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");  
  105.   
  106.             ngx_worker_process_exit(cycle);  
  107.         }  
  108.   
  109.         /* worker进程收到了SIGQUIT信号,如果此时worker进程不是不是处于exiting状态,  
  110.          * 就将设置ngx_exiting为1,让其进入exiting状态;同时关闭监听套接口,待看...  
  111.          */  
  112.         if (ngx_quit) {  
  113.             ngx_quit = 0;  
  114.             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,  
  115.                           "gracefully shutting down");  
  116.             ngx_setproctitle("worker process is shutting down");  
  117.   
  118.             if (!ngx_exiting) {  
  119.                 ngx_close_listening_sockets(cycle);  
  120.                 ngx_exiting = 1;  
  121.             }  
  122.         }  
  123.   
  124.         /* worker进程收到了SIGUSR1信号,通知Nginx需要重新打开文件 */  
  125.         if (ngx_reopen) {  
  126.             ngx_reopen = 0;  
  127.             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");  
  128.             ngx_reopen_files(cycle, -1);  
  129.         }  
  130.     }  
  131. }  
下面是ngx_worker_process_init()的工作:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static void  
  2. ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)  
  3. {  
  4.     sigset_t          set;  
  5.     uint64_t          cpu_affinity;  
  6.     ngx_int_t         n;  
  7.     ngx_uint_t        i;  
  8.     struct rlimit     rlmt;  
  9.     ngx_core_conf_t  *ccf;  
  10.     ngx_listening_t  *ls;  
  11.   
  12.     /* 设置全局的环境变量environ,不细看了,细看暂时也弄不明白 */  
  13.     if (ngx_set_environment(cycle, NULL) == NULL) {  
  14.         /* fatal */  
  15.         exit(2);  
  16.     }  
  17.   
  18.     /* 获取核心模块配置 */  
  19.     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  20.   
  21.     /* 设置worker进程优先级 */  
  22.     if (worker >= 0 && ccf->priority != 0) {  
  23.         if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {  
  24.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  25.                           "setpriority(%d) failed", ccf->priority);  
  26.         }  
  27.     }  
  28.       
  29.     /* 这里是给成员做资源使用的限制,可以查下setrlimit()系统调用 */  
  30.     if (ccf->rlimit_nofile != NGX_CONF_UNSET) {  
  31.         rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;  
  32.         rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;  
  33.   
  34.         if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {  
  35.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  36.                           "setrlimit(RLIMIT_NOFILE, %i) failed",  
  37.                           ccf->rlimit_nofile);  
  38.         }  
  39.     }  
  40.   
  41.     if (ccf->rlimit_core != NGX_CONF_UNSET) {  
  42.         rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;  
  43.         rlmt.rlim_max = (rlim_t) ccf->rlimit_core;  
  44.   
  45.         if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {  
  46.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  47.                           "setrlimit(RLIMIT_CORE, %O) failed",  
  48.                           ccf->rlimit_core);  
  49.         }  
  50.     }  
  51.   
  52. #ifdef RLIMIT_SIGPENDING  
  53.     if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {  
  54.         rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;  
  55.         rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;  
  56.   
  57.         if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {  
  58.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  59.                           "setrlimit(RLIMIT_SIGPENDING, %i) failed",  
  60.                           ccf->rlimit_sigpending);  
  61.         }  
  62.     }  
  63. #endif  
  64.   
  65.     /* geteuid()用于获取执行目前进程有效的用户识别码,root用户的uid为0,这里是说如果拥有root超级权限 */  
  66.     if (geteuid() == 0) {  
  67.         /* 设置组ID,目的猜测是多个worker进程共享同一个目录下的文件 */  
  68.         if (setgid(ccf->group) == -1) {  
  69.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,  
  70.                           "setgid(%d) failed", ccf->group);  
  71.             /* fatal */  
  72.             exit(2);  
  73.         }  
  74.   
  75.         /* 在user中设置组ID,猜测 */  
  76.         if (initgroups(ccf->username, ccf->group) == -1) {  
  77.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,  
  78.                           "initgroups(%s, %d) failed",  
  79.                           ccf->username, ccf->group);  
  80.         }  
  81.   
  82.         /* 将worker进程设置为拥有该文件所有者同样的权限 */  
  83.         if (setuid(ccf->user) == -1) {  
  84.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,  
  85.                           "setuid(%d) failed", ccf->user);  
  86.             /* fatal */  
  87.             exit(2);  
  88.         }  
  89.     }  
  90.   
  91.     /* 这里九分猜测是给worker进程分配CPU */  
  92.     if (worker >= 0) {  
  93.         cpu_affinity = ngx_get_cpu_affinity(worker);  
  94.   
  95.         if (cpu_affinity) {  
  96.             ngx_setaffinity(cpu_affinity, cycle->log);  
  97.         }  
  98.     }  
  99.   
  100. #if (NGX_HAVE_PR_SET_DUMPABLE)  
  101.   
  102.     /* allow coredump after setuid() in Linux 2.4.x */  
  103.   
  104.     if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {  
  105.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  106.                       "prctl(PR_SET_DUMPABLE) failed");  
  107.     }  
  108.   
  109. #endif  
  110.   
  111.     /* 这里是更改工作路径 */  
  112.     if (ccf->working_directory.len) {  
  113.         if (chdir((char *) ccf->working_directory.data) == -1) {  
  114.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  115.                           "chdir(\"%s\") failed", ccf->working_directory.data);  
  116.             /* fatal */  
  117.             exit(2);  
  118.         }  
  119.     }  
  120.   
  121.     /* 初始化信号集 */  
  122.     sigemptyset(&set);  
  123.   
  124.     /* 将set集中的信号设为阻塞。 
  125.      * 这里是让worker进程对set集中的信号延迟处理 
  126.      */  
  127.     if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {  
  128.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  129.                       "sigprocmask() failed");  
  130.     }  
  131.   
  132.     /* 
  133.      * disable deleting previous events for the listening sockets because 
  134.      * in the worker processes there are no events at all at this point 
  135.      */  
  136.     /* worker子进程所有的监听端口初始化 */  
  137.     ls = cycle->listening.elts;  
  138.     for (i = 0; i < cycle->listening.nelts; i++) {  
  139.         ls[i].previous = NULL;  
  140.     }  
  141.   
  142.     /* worker子进程进行所有模块的自定义初始化 */  
  143.     for (i = 0; ngx_modules[i]; i++) {  
  144.         if (ngx_modules[i]->init_process) {  
  145.             if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {  
  146.                 /* fatal */  
  147.                 exit(2);  
  148.             }  
  149.         }  
  150.     }  
  151.   
  152.     /* ngx_processes[]是一个所有worker进程共享的全局变量,这里为了保证当前worker进程 
  153.      * 禁止读取master进程给其他worker进程的消息,所以这里必须关闭所有其他worker进程的读端描述符。 
  154.      * 然后,保留对其他worker进程的写端描述符和自身的读端描述符,目的是能够保持所有worker进程间的通信。 
  155.      * 其他所有worker进程也都如此做。一句话就是,通过其他worker进程写端写入消息给别人,通过自身读端来接收来自master 
  156.      * 和其他worker进程的消息 
  157.      */  
  158.     for (n = 0; n < ngx_last_process; n++) {  
  159.   
  160.         /* 不存在的worker进程,跳过 */  
  161.         if (ngx_processes[n].pid == -1) {  
  162.             continue;  
  163.         }  
  164.   
  165.         /* 该worker进程本身,跳过 */  
  166.         if (n == ngx_process_slot) {  
  167.             continue;  
  168.         }  
  169.           
  170.         /* channel[1]已关闭,跳过 */  
  171.         if (ngx_processes[n].channel[1] == -1) {  
  172.             continue;  
  173.         }  
  174.   
  175.         /* 对其他worker进程的读端文件描述符关闭,保留写端文件描述符保持worker间的通信之用 */  
  176.         if (close(ngx_processes[n].channel[1]) == -1) {  
  177.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  178.                           "close() channel failed");  
  179.         }  
  180.     }  
  181.   
  182.     /* 关闭自身的写端文件描述符,因为每个worker进程只需要从自己的读端读取消息,而不用给自己写消息。 
  183.      * 需要用到自身的写端文件描述符的是master和其他worker进程。这也是上面为什么要关闭其他worker进程的读端,保留写端的原因。 
  184.     if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { 
  185.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
  186.                       "close() channel failed"); 
  187.     } 
  188.  
  189. #if 0 
  190.     ngx_last_process = 0; 
  191. #endif 
  192.  
  193.     /* 这里是令Nginx开始关注当前worker进程中channel读端的读事件,下面稍微描述下,现在还不能一窥全景 */  
  194.     if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,  
  195.                               ngx_channel_handler)  
  196.         == NGX_ERROR)  
  197.     {  
  198.         /* fatal */  
  199.         exit(2);  
  200.     }  
  201. }  
  202.   
  203. ngx_int_t  
  204. ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, ngx_int_t event,  
  205.     ngx_event_handler_pt handler)  
  206. {  
  207.     ngx_event_t       *ev, *rev, *wev;  
  208.     ngx_connection_t  *c;  
  209.   
  210.     /* 这些个参数配置看是看了,但暂时不清楚怎么用,以后再来说 */  
  211.     c = ngx_get_connection(fd, cycle->log);  
  212.   
  213.     if (c == NULL) {  
  214.         return NGX_ERROR;  
  215.     }  
  216.   
  217.     c->pool = cycle->pool;  
  218.   
  219.     rev = c->read;  
  220.     wev = c->write;  
  221.   
  222.     rev->log = cycle->log;  
  223.     wev->log = cycle->log;  
  224.   
  225. #if (NGX_THREADS)  
  226.     rev->lock = &c->lock;  
  227.     wev->lock = &c->lock;  
  228.     rev->own_lock = &c->lock;  
  229.     wev->own_lock = &c->lock;  
  230. #endif  
  231.   
  232.     rev->channel = 1;  
  233.     wev->channel = 1;  
  234.   
  235.     ev = (event == NGX_READ_EVENT) ? rev : wev;  
  236.   
  237.     ev->handler = handler;  
  238.   
  239.     if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {  
  240.         if (ngx_add_conn(c) == NGX_ERROR) {  
  241.             ngx_free_connection(c);  
  242.             return NGX_ERROR;  
  243.         }  
  244.   
  245.     } else {  
  246.         /* 猜的不错,这个应该是重点,调用ngx_add_event宏,在这里如果是epoll的话, 
  247.          * 应该是调用ngx_epoll_add_event函数,将该channel上的读事件加入到epoll 
  248.          */  
  249.         if (ngx_add_event(ev, event, 0) == NGX_ERROR) {  
  250.             ngx_free_connection(c);  
  251.             return NGX_ERROR;  
  252.         }  
  253.     }  
  254.   
  255.     return NGX_OK;  
  256. }  

总结

分析源码要考虑前因后果,单单停留在代码的表面表示会有很多难度。看完这个worker循环,对信号量的设置,以及进程初始化的工作有了个初步的认识。另外,就单独对于代码技巧方面,比如加入epoll事件等等。。。Nginx确实是一套很牛的代码。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值