worker进程是nginx中负责干活的进程. 下面来看worker进程是如何产生的.
ngx_start_worker_processes--->ngx_spawn_process
-
ngx_spawn_process(cycle, ngx_worker_process_cycle,
-
(void *) (intptr_t) i, "worker process", type);
ngx_worker_process_cycle是fork出来的worker进程将要执行的函数入口.
下面来看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;
-
}
为该worker进程在ngx_processes中找一个空闲槽.
-
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;
-
}
创建一对已连接的UNIX域套接字. 用于和master进程通信.
-
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)
1,2将返回的两个套接字描述符设置为非阻塞的. 3, 4将设置第一个描述符的异步IO. 当该描述符状态发生改变时,
会向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;
-
}
fork子进程, 该子进程即作为worker进程, 保存worker进程的进程ID之后立马调用ngx_worker_process_cycle.
后面的代码保存worker进程的一些信息:如进程ID, 入口函数, 私有数据等. 忽略不仔细分析了.
下面来看真正的worker进程是如何开始工作的.
ngx_worker_process_cycle函数:
-
ngx_worker_process_init(cycle, worker);
初始化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);
-
}
-
}
geteuid获取进程的有效UID. 若是以root身份运行nginx或者nginx是setuid. 则将worker进程降级为ccf->user 级 ccf->group权限. 默认是nobody.
这里可知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);
-
}
-
}
-
}
调用所有模块的init_process方法来初始化各模块的核心功能. 主要是ngx_event_core_module会调用所使用的epoll模块的action.init方法.来初始化epoll.详见
[nginX-1.7.2] event核心模块结构体
-
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[1]这个打开的套接字描述符. 除了自己用于与master进程通信的那个描述符.
然后关闭属于该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[1]加入到事件驱动模型, 即epoll中. 关注的改套接字上的读事件.
这样当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");
设置该进程的进程名称为"worker process", argv[0]即保存的是进程名.
接下来就是一个for(;;)死循环. 最主要的就是ngx_process_events_and_timers的调用:
-
ngx_process_events_and_timers(cycle);
ngx_process_events_and_timers中最核心的就是:
-
(void) ngx_process_events(cycle, timer, flags);
对于使用epoll模块, 其调用实质上是ngx_epoll_module.actions.ngx_epoll_process_events.
改函数调用epoll_wait来等待各种注册的事件的发生. 默认情况是永远等待.
再次可以看出整个worker进程的核心就是epoll模块的初始化. 将监听套接字描述符以及用于和master通信的描述符加入到epoll
中. 通过epoll_wait来侦听所有关注的事件进行异步处理.
阅读(4470) | 评论(0) | 转发(0) |