依据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);


/*...*/
}

/*...*/
}

nginx中父子进程工作的主体函数的更多相关文章

  1. 使用信号管理nginx的父子进程

    master进程 通过CHLD监控worker进程,worker异常退出,通过CHLD信号拉起worker进程. 接收信号 TERM,INT信号表示立刻停止worker进程 QUIT信号表示优雅的停止 ...

  2. Nginx中的进程亲和性 affinity

    Nginx采用多进程Master/Worker结构,Worker进程数为CPU个数时工作效率最高,Nginx通过affinity为每个Worker进程绑定一个CPU,避免进程切换带来的消耗,同时能够保 ...

  3. UNIX环境编程学习笔记(19)——进程管理之fork 函数的深入学习

    lienhua342014-10-07 在“进程控制三部曲”中,我们学习到了 fork 是三部曲的第一部,用于创建一个新进程.但是关于 fork 的更深入的一些的东西我们还没有涉及到,例如,fork ...

  4. Nginx之监控进程和工作进程

    1. 函数调用分析 在开启 master 的情况下,多进程模型的下的入口函数为 ngx_master_process_cycle,如下: int mian() { ... if (ngx_proces ...

  5. linux 中的进程wait()和waitpid函数,僵尸进程详解,以及利用这两个函数解决进程同步问题

    转载自:http://blog.sina.com.cn/s/blog_7776b9d3010144f9.html 在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / wait ...

  6. Nginx源码分析:3张图看懂启动及进程工作原理

    编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」.   导读:很多工程师及架构师都希望了解及掌握高性能服务器 ...

  7. 关于fork( )函数父子进程返回值的问题

    fork()是linux的系统调用函数sys_fork()的提供给用户的接口函数,fork()函数会实现对中断int 0x80的调用过程并把调用结果返回给用户程序. fork()的函数定义是在init ...

  8. python中进程池和回调函数

    一.数据共享 1.进程间的通信应该尽量避免共享数据的方式 2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实 ...

  9. IIS6与IIS7中的w3wp工作进程

    在IIS6中,每一个网站都有对应的应用程序池,在应用程序池有运行着网站的Application,在默认情况下,所有的网站的应用程序都会分配到默认的应用程序池当中,   当然,我们可以新建一个应用程序池 ...

随机推荐

  1. 【STM32H7教程】第23章 STM32H7的MPU内存保护单元(重要)

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第23章       STM32H7的MPU内存保护单元 ...

  2. JNI学习积累之一 ---- 常用函数大全

    主要资料来源: 百度文库的<JNI常用函数> . 同时对其加以了补充 . 要素  :1. 该函数大全是基于C语言方式的,对于C++方式可以直接转换 ,例如,对于生成一个jstring类型的 ...

  3. SQLServer2008 将“单个用户”改为“多用户”

    一开始是要想要分离掉数据库,然后将其删除 不知道为什么一直分离不了,试了很多次,又尝试直接删除 结果数据库突然显示成了“单个用户” 尝试查看其属性,或者“新建查询”也都报错,提示已经有其他用户建立了连 ...

  4. OpenCV:OpenCV目标检测Adaboost+haar源代码分析

    使用OpenCV作图像检测, Adaboost+haar决策过程,其中一部分源代码如下: 函数调用堆栈的底层为: 1.使用有序决策桩进行预测 template<class FEval> i ...

  5. Matlab/Eigen矩阵填充问题

    Matlab进行矩阵填充时可以填充空矩阵,相当于空矩阵不存在,例如一下代码: P_RES = [ P_xv P_xvy P_xv*dy_dxv'; P_yxv P_y P_yxv*dy_dxv'; d ...

  6. 通过Static 字段来维护状态是不是一个好主意

    static是申明静态字段.静态方法或者静态类的修饰符.使用static申明的字段属于类型本身而不属于任何字段,声明的类也具有一些特别特性,比如不能实例化,不能继承等.用通俗化的语言来说,static ...

  7. (转)基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式

    http://www.cnblogs.com/wuhuacong/p/4085682.html 在基于MVC4+EasyUI的Web开发框架里面,大量采用了Jquery的方法,对数据进行请求或者提交, ...

  8. (转)Bootstrap 之 Metronic 模板的学习之路 - (5)主题&布局配置

    https://segmentfault.com/a/1190000006736457 Theme Setup 主题配置 Metronic comes with 6 color themes,defa ...

  9. 记Python学习

    上周学的Python,感觉有点忘了,现在回顾一下... 一.Python安装及测试:https://www.cnblogs.com/weven/p/7252917.html 例子: Python自带的 ...

  10. MessageFormat.format()用法

    1.java.text.Format的继承结构如下   2.MessageFormat模式 FormatElement { ArgumentIndex }:是从0开始的入参位置索引 { Argumen ...