ngx_start_worker_processes--->ngx_spawn_process
点击(此处)折叠或打开
-
ngx_spawn_process(cycle, ngx_worker_process_cycle,
- (void *) (intptr_t) i, "worker process", type);
下面来看nx_spawn_process:
点击(此处)折叠或打开
-
for (s = 0; s < ngx_last_process; s++) {
-
if (ngx_processes[s].pid == -1) {
-
break;
-
}
-
}
-
-
if (s == NGX_MAX_PROCESSES) {
-
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
-
"no more than %d processes can be spawned",
-
NGX_MAX_PROCESSES);
-
return NGX_INVALID_PID;
- }
点击(此处)折叠或打开
-
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
-
{
-
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
-
"socketpair() failed while spawning \"%s\"", name);
-
return NGX_INVALID_PID;
- }
点击(此处)折叠或打开
- ngx_nonblocking(ngx_processes[s].channel[0])
- ngx_nonblocking(ngx_processes[s].channel[1])
- ioctl(ngx_processes[s].channel[0], FIOASYNC, &on)
- fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid)
- fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC)
-
fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC)
会向ngx_pid发送SIGIO信号.ngx_pid是master进程的进程ID. 5, 6设置连个描述符的CLOEXEC标识.
由于套接字是全双工的. master进程用channel[0]描述符来接受和发送消息. worker进程用channel[1]来接收和发送消息.
点击(此处)折叠或打开
-
pid = fork();
-
-
switch (pid) {
-
-
case -1:
-
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
-
"fork() failed while spawning \"%s\"", name);
-
ngx_close_channel(ngx_processes[s].channel, cycle->log);
-
return NGX_INVALID_PID;
-
-
case 0:
-
ngx_pid = ngx_getpid();
-
proc(cycle, data);
-
break;
-
-
default:
-
break;
- }
后面的代码保存worker进程的一些信息:如进程ID, 入口函数, 私有数据等. 忽略不仔细分析了.
下面来看真正的worker进程是如何开始工作的.
ngx_worker_process_cycle函数:
点击(此处)折叠或打开
- ngx_worker_process_init(cycle, worker);
ngx_worker_process_init:
点击(此处)折叠或打开
-
if (geteuid() == 0) {
-
if (setgid(ccf->group) == -1) {
-
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-
"setgid(%d) failed", ccf->group);
-
/* fatal */
-
exit(2);
-
}
-
-
if (initgroups(ccf->username, ccf->group) == -1) {
-
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-
"initgroups(%s, %d) failed",
-
ccf->username, ccf->group);
-
}
-
-
if (setuid(ccf->user) == -1) {
-
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-
"setuid(%d) failed", ccf->user);
-
/* fatal */
-
exit(2);
-
}
- }
这里可知worker进程是以非特权用户进程运行的. 关于initgroups设置的权限见这篇关于附加组ID initgroups的一点探索
点击(此处)折叠或打开
-
for (i = 0; ngx_modules[i]; i++) {
-
if (ngx_modules[i]->init_process) {
-
if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
-
/* fatal */
-
exit(2);
-
}
-
}
- }
点击(此处)折叠或打开
-
for (n = 0; n < ngx_last_process; n++) {
-
-
if (ngx_processes[n].pid == -1) {
-
continue;
-
}
-
-
if (n == ngx_process_slot) {
-
continue;
-
}
-
-
if (ngx_processes[n].channel[1] == -1) {
-
continue;
-
}
-
-
if (close(ngx_processes[n].channel[1]) == -1) {
-
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
-
"close() channel failed");
-
}
- }
- }
-
if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() channel failed");
}
然后关闭属于该worker进程的channel[0]套接字描述符. 这个描述符是由master进程所使用的.
点击(此处)折叠或打开
-
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
-
ngx_channel_handler)
-
== NGX_ERROR)
-
{
-
/* fatal */
-
exit(2);
- }
这样当master进程通过channel[0]发送消息时, 通过epoll的通知, 异步调用ngx_channel_handler处理方法.
ngx_worker_process_init总结:
1.若以特权身份运行nginx. 则设置worker进程以非特权身份运行.
2.调用所有模块的init_process方法. 关键是ngx_event_core_module. 以及ngx_epoll_module初始化epoll.
3.关闭不需要的用于通信的套接字描述符.
4.将用于与master通信的套接字描述符加入到epoll中. 关注起读事件.
下面回到ngx_worker_process_cycle:
点击(此处)折叠或打开
- ngx_setproctitle("worker process");
接下来就是一个for(;;)死循环. 最主要的就是ngx_process_events_and_timers的调用:
点击(此处)折叠或打开
- ngx_process_events_and_timers(cycle);
点击(此处)折叠或打开
- (void) ngx_process_events(cycle, timer, flags);
改函数调用epoll_wait来等待各种注册的事件的发生. 默认情况是永远等待.
再次可以看出整个worker进程的核心就是epoll模块的初始化. 将监听套接字描述符以及用于和master通信的描述符加入到epoll
中. 通过epoll_wait来侦听所有关注的事件进行异步处理.