如不做特殊说明,本博客所使用的 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 }
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 的精髓!>_<