Chinaunix首页 | 论坛 | 博客
  • 博客访问: 379976
  • 博文数量: 56
  • 博客积分: 1449
  • 博客等级: 中尉
  • 技术积分: 822
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-08 10:24
文章分类

全部博文(56)

文章存档

2014年(7)

2012年(13)

2011年(10)

2010年(26)

分类: 服务器与存储

2014-08-14 16:36:10

worker进程是nginx中负责干活的进程. 下面来看worker进程是如何产生的.

ngx_start_worker_processes--->ngx_spawn_process

点击(此处)折叠或打开

  1. ngx_spawn_process(cycle, ngx_worker_process_cycle,
  2.                           (void *) (intptr_t) i, "worker process", type);
ngx_worker_process_cycle是fork出来的worker进程将要执行的函数入口.
下面来看nx_spawn_process:

点击(此处)折叠或打开

  1. for (s = 0; s < ngx_last_process; s++) {
  2.             if (ngx_processes[s].pid == -1) {
  3.                 break;
  4.             }
  5.         }

  6.         if (s == NGX_MAX_PROCESSES) {
  7.             ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
  8.                           "no more than %d processes can be spawned",
  9.                           NGX_MAX_PROCESSES);
  10.             return NGX_INVALID_PID;
  11.         }
为该worker进程在ngx_processes中找一个空闲槽.

点击(此处)折叠或打开

  1. if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
  2.         {
  3.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
  4.                           "socketpair() failed while spawning \"%s\"", name);
  5.             return NGX_INVALID_PID;
  6.         }
创建一对已连接的UNIX域套接字. 用于和master进程通信.

点击(此处)折叠或打开

  1. ngx_nonblocking(ngx_processes[s].channel[0])
  2. ngx_nonblocking(ngx_processes[s].channel[1])
  3. ioctl(ngx_processes[s].channel[0], FIOASYNC, &on)
  4. fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid)
  5. fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC)
  6. 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]来接收和发送消息.

点击(此处)折叠或打开

  1. pid = fork();

  2.     switch (pid) {

  3.     case -1:
  4.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
  5.                       "fork() failed while spawning \"%s\"", name);
  6.         ngx_close_channel(ngx_processes[s].channel, cycle->log);
  7.         return NGX_INVALID_PID;

  8.     case 0:
  9.         ngx_pid = ngx_getpid();
  10.         proc(cycle, data);
  11.         break;

  12.     default:
  13.         break;
  14.     }
fork子进程, 该子进程即作为worker进程, 保存worker进程的进程ID之后立马调用ngx_worker_process_cycle.
后面的代码保存worker进程的一些信息:如进程ID, 入口函数, 私有数据等. 忽略不仔细分析了.

下面来看真正的worker进程是如何开始工作的.
ngx_worker_process_cycle函数:

点击(此处)折叠或打开

  1. ngx_worker_process_init(cycle, worker);
初始化worker进程执行环境. 下面来详细分析这个函数.
ngx_worker_process_init:

点击(此处)折叠或打开

  1. if (geteuid() == 0) {
  2.         if (setgid(ccf->group) == -1) {
  3.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  4.                           "setgid(%d) failed", ccf->group);
  5.             /* fatal */
  6.             exit(2);
  7.         }

  8.         if (initgroups(ccf->username, ccf->group) == -1) {
  9.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  10.                           "initgroups(%s, %d) failed",
  11.                           ccf->username, ccf->group);
  12.         }

  13.         if (setuid(ccf->user) == -1) {
  14.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  15.                           "setuid(%d) failed", ccf->user);
  16.             /* fatal */
  17.             exit(2);
  18.         }
  19.     }
geteuid获取进程的有效UID. 若是以root身份运行nginx或者nginx是setuid. 则将worker进程降级为ccf->user 级 ccf->group权限. 默认是nobody.
这里可知worker进程是以非特权用户进程运行的. 关于initgroups设置的权限见这篇关于附加组ID initgroups的一点探索

点击(此处)折叠或打开

  1. for (i = 0; ngx_modules[i]; i++) {
  2.         if (ngx_modules[i]->init_process) {
  3.             if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
  4.                 /* fatal */
  5.                 exit(2);
  6.             }
  7.         }
  8.     }
调用所有模块的init_process方法来初始化各模块的核心功能. 主要是ngx_event_core_module会调用所使用的epoll模块的action.init方法.来初始化epoll.详见[nginX-1.7.2] event核心模块结构体

点击(此处)折叠或打开

  1. for (n = 0; n < ngx_last_process; n++) {

  2.         if (ngx_processes[n].pid == -1) {
  3.             continue;
  4.         }

  5.         if (n == ngx_process_slot) {
  6.             continue;
  7.         }

  8.         if (ngx_processes[n].channel[1] == -1) {
  9.             continue;
  10.         }

  11.         if (close(ngx_processes[n].channel[1]) == -1) {
  12.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
  13.                           "close() channel failed");
  14.         }
  15.     }
  16. }
  17. 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进程所使用的.

点击(此处)折叠或打开

  1. if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
  2.                               ngx_channel_handler)
  3.         == NGX_ERROR)
  4.     {
  5.         /* fatal */
  6.         exit(2);
  7.     }
将用于与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:

点击(此处)折叠或打开

  1. ngx_setproctitle("worker process");
设置该进程的进程名称为"worker process", argv[0]即保存的是进程名.

接下来就是一个for(;;)死循环. 最主要的就是ngx_process_events_and_timers的调用:

点击(此处)折叠或打开

  1. ngx_process_events_and_timers(cycle);
ngx_process_events_and_timers中最核心的就是:

点击(此处)折叠或打开

  1. (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) |
0

上一篇:[nginX-1.7.2] event核心模块结构体

下一篇:没有了

给主人留下些什么吧!~~