设为首页 收藏本站
查看: 655|回复: 0

[经验分享] Nginx父子进程的创建及主体工作函数

[复制链接]

尚未签到

发表于 2016-12-28 07:56:52 | 显示全部楼层 |阅读模式
  根据Nginx(0.7.67版本)的代码,对Nginx基本的进程创建,进程主体以及事件处理进行了简要的分析。




基本上,父进程(即主进程)一开始会初始化及读取配置,并加载各模块的功能,然后fork()出N个子进程(即工作进程),具有相同的工作逻辑和功能。父进程负责监听信号(如HUP,QUIT等),通过socket pair把信号传递给子进程(子进程间一般不通信)。子进程通过事件来处理父进程传递的信号。因为每个子进程都共享服务监听端口(如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++) {
ngx_modules->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,
//bind/listen 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);
}
}
 









//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.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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-320344-1-1.html 上篇帖子: Nginx 负载均衡 apache https 之 keepalived 配置 下篇帖子: 用AWK来过滤nginx日志中的特定值~~~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表