依据Nginx(0.7.67版本号)的代码。对Nginx主要的进程创建,进程主体以及事件处理进行了简要的分析。


基本上,父进程(即主进程)一開始会初始化及读取配置。并载入各模块的功能,然后fork()出N个子进程(即工作进程),具有同样的工作逻辑和功能。

父进程负责监听信号(如HUP,QUIT等),通过socket pair把信号传递给子进程(子进程间一般不通信)。子进程通过事件来处理父进程传递的信号。由于每一个子进程都共享服务监听port(如http 80),当用户发送请求时,会触发子进程的事件调用函数。因此在accept()请求的时候,须要用到mutex,保证仅仅有一个工作进程接受并处理请求。


Nginx主进程的入口是跟一般的程序一样的main()函数。它的代码是这种:
int ngx_cdecl main(int argc, char *const *argv)
{
/*...*/

//nginx 启动的时候会尝试从环境变量中读取前次运行时候的监听套接口的id,
//并会创建相应数量的ngx_listening_t结构变量(存于 cycle->listening数组中)。
//然后调用这个接口通过getsockname,getsockopt等系统调用把原来套接口的属性信息和
//设置參数读取出来去设置那些新创建的ngx_listening_t结构变量。这样就继承了前次运行
//时候的监听套接口了。这个接口是在 ngx_init_cycle之前调用的

if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) { 
return 1; }

/*...*/

//ngx_modules数组在objs/ngx_modules.c定义ngx_max_module = 0;for (i = 0; ngx_modules[i]; i++) { ngx_modules[i]->index = ngx_max_module++; //将每一个模块编号
}

//ngx_init_cycle()函数,这是个比較重要的函数。被main, ngx_master_process_cycle
//和ngx_single_process_cycle 调用, 当中后两者是在reconfigure的时候被调用的。
//它主要工作是。初始化cycle是基于旧有的cycle进行的;
会继承old cycle的非常多属性。 
//比方log等, 可是同一时候会对非常多资源又一次分配,
比方pool, shared mem, file handler, 
//listening socket等,同一时候清除旧有的cycle的资源
//读取配置文件也是在这里完毕的
cycle = ngx_init_cycle(&init_cycle);

if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); } else {
//一般採用多进程处理请求
ngx_master_process_cycle(cycle);
}
}

//主进程(即父进程)的主体
//这个函数会启动工作进程干活。而且会处理信号量。处理的过程中会杀死或者创建新的进程
void ngx_master_process_cycle(ngx_cycle_t *cycle)
{
//把信号添加到监听集合set sigemptyset(&set);//sigemptyset()用来将參数set信号集初始化并清空 sigaddset(&set, SIGCHLD);//sigaddset() 添加一个信号至信号集 sigaddset(&set, SIGALRM); sigaddset(&set, SIGIO); sigaddset(&set, SIGINT);
sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));

/*...*/

//依照ngx_core_conf_t中worker_processes数,启动若干个work进程 ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN);

//后面一个循环对不同的状态进行不同处理。而那些状态多数是进程收到的不同信号//ngx_signal_t signals[]数组定义信号以及处理方法:ngx_signal_handler()//信号在ngx_init_signals()里面初始化//在ngx_signal_handler()里,依据ngx_process来决定是master
process还是
//worker process的处理流程(即是说master和worker都调用ngx_signal_handler
//来处理信号)

for ( ;; ) {
//挂起当前进程,等到有信号。就会从挂起状态退出。继续运行//set记录当前监听的信号 sigsuspend(&set);
/*各种信号的处理,如NGX_SHUTDOWN_SIGNAL,NGX_RECONFIGURE_SIGNAL等*/
/*省略*/
}
}


//ngx_start_worker_processes()函数。这个函数按指定数目n,以ngx_worker_process_cycle()函
//数为參数调用ngx_spawn_process()创建work进程并初始化相关资源和属性。
//运行子进程的运行函数ngx_worker_process_cycle;向之前已经创建的全部worker广播当前创建的
//worker进程的信息。每一个进程打开一个通道(ngx_pass_open_channel())
//n是worker process的数目
//type 即创建新进程式的方式,如NGX_PROCESS_RESPAWN, NGX_PROCESS_JUST_RESPAWN
static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
     ngx_int_t      i;
ngx_channel_t  ch;

     ch.command = NGX_CMD_OPEN_CHANNEL;

     for (i = 0; i < n; i++) {
//从config读取CPU的分配
         cpu_affinity = ngx_get_cpu_affinity(i);
//ngx_spawn_process中设置ngx_process_slot为被分配到子进程的
//空暇slot
         ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
"worker process", type);
         ch.pid = ngx_processes[ngx_process_slot].pid;
         ch.slot = ngx_process_slot;
         ch.fd = ngx_processes[ngx_process_slot].channel[0];
//ngx_pass_open_channel 把这个worker的channel[0]和进程id
//在进程表中的偏移slot广播(ngx_write_channel())给全部其它已经
//创建的worker。

//这样,创建全然部进程之后,每一个worker就获得了全部其它worker的
//channel[0]了
         ngx_pass_open_channel(cycle, &ch);
    }
}


ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn)
{
/*...*/
//假设类型为NGX_PROCESS_DETACHED。则说明是热代码替换(热代码替换也是通过这
//个函数进行处理的),因此不须要新建socketpair。

if (respawn != NGX_PROCESS_DETACHED) {//建立socketpair //创建socketpair用于进程间通信。master进程为每一个worker创建一对
//socket, master进程空间打开全部socketpair的channel[0],
//channel[1]两端句柄。

//当创建一个worker的时候,这个worker会继承master当前已经创建并

//打开的全部socketpair。//这个worker初始化的时候(调用ngx_worker_process_init)会关闭
//本进程相应socketpair的channel[0]和其它worker相应的
//channel[1],保持打开本进程相应socketpair的channel[1]和其它
//worker相应的channel[0],并监听本进程相应socketpair的
//channel[1]的可读事件。这样,每一个worker就拥有了其它worker的
//channel[0],能够sendmsg(channel[0], ...)向其它worker发送消息
/*...*/
}

/*...*/
//创建子进程
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();
//调用ngx_worker_process_cycle()
//注意:这个函数中定义了子进程处理事件的循环,即子进程不会
//运行这个函数之后的语句
proc(cycle, data);break;default:break; }

}

//worker进程的主体
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
//设置一些环境变量
//调用全部模块的init_process钩子函数
//将其它进程的channel[1]关闭。自己的除外//子进程继承了父进程的ngx_processes数组。但子进程仅仅监听自己的channel[1]
//将自己的channel[0]关闭//由于自己的channel[0]是给其它子进程。用来发送消息的sendmsg
//调用ngx_add_channel_event()函数,给ngx_channel注冊一个读事件处理函数。
ngx_worker_process_init(cycle, 1);
//主循环。处理事件
for ( ;; ) {
/*...*/

//调用epoll,周期性监听事件
//先接收连接(并不处理事件)。以及处理进程间信号(如有)
//处理accept queue和event queue里面的事件

ngx_process_events_and_timers(cycle);


/*...*/
}

/*...*/
}

最新文章

  1. 我的MYSQL学习心得(十一) 视图
  2. iOS:frame访问、设置简化
  3. java高新技术-枚举
  4. Debian使用相关
  5. salesforce 零基础学习(三十一)关于LookUp字段点击Save时的Validation
  6. 免费 PSD 下载: 20个精美的登录和注册表单
  7. 帝国CMS内容模板IF判断
  8. [转]SecureCRT连接主机时,无法从键盘输入
  9. 【leetcode】Best Time to Buy and Sell Stock III
  10. webim-界面细节调整
  11. Jmeter之Bean shell使用(二)
  12. 获取星座的JS函数
  13. numba学习教程
  14. eclipse 中修改项目名称,启动tomct原项目找不到的问题
  15. Unified Temporal and Spatial Calibration for Multi-Sensor Systems
  16. WebRTC 学习之 概念总结
  17. JS发送短信验证码
  18. day2作业(基本数据类型,语法)
  19. 1079. Total Sales of Supply Chain (25)-求数的层次和叶子节点
  20. 针对Jigsaw勒索软件的解锁工具

热门文章

  1. Oracle 字符拼接
  2. Python 45 长度及颜色单位 、字体样式 、文本样式 、背景样式 、css基础选择器
  3. Redis的事务讲解
  4. HDU 1054 Hungary
  5. (转)Win10 TensorFlow(gpu)安装详解
  6. SQL连接其它服务器操作
  7. 删除django
  8. retrofit post请求多文件,partMap用法
  9. Md2All,让公众号完美显示Latex数学公式
  10. C# 7.0新加特性