Nginx学习笔记(七) 创建子进程
Nginx创建子进程
ngx_start_worker_processes位于Nginx_process_cycle.c中,主要的工作是创建子进程。
在Nginx中,master进程和worker进程是通过socketpair函数创建一对socket来实现,父进程与子进程之间的通信的。而这对socket被保存在进程结构体ngx_process中的channel[2]数组中,其中channel[0]为父进程的socket,channel[1]为子进程的socket。
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; ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "start worker processes");
//传递给其他进程的命令
ch.command = NGX_CMD_OPEN_CHANNEL; for (i = ; i < n; i++) {
//创建n个子进程
ngx_spawn_process(cycle, ngx_worker_process_cycle,
(void *) (intptr_t) i, "worker process", type);
//保存当前worker进程的信息
ch.pid = ngx_processes[ngx_process_slot].pid;
ch.slot = ngx_process_slot; //ngx_process_slot是进程信息在全局进程数组中存放的下标
ch.fd = ngx_processes[ngx_process_slot].channel[];//channel[0]为父进程的socket,channel[1]为子进程的socket
//向每一个进程的父进程发送本进程的信息
ngx_pass_open_channel(cycle, &ch);
}
具体分析一下创建子进程的函数,也就是分析ngs_spawn_process:
ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn)
{
u_long on;
ngx_pid_t pid;
ngx_int_t s; //可创建进程的位置 if (respawn >= ) {
s = respawn; //如果类型大于0,表示该进程已经退出,可以重启该进程 } else {
for (s = ; s < ngx_last_process; s++) { //遍历所有进程,找到可用的已退出的进程
if (ngx_processes[s].pid == -) {
break;
}
}
//超过最大进程限制会报错
if (s == NGX_MAX_PROCESSES) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ,
"no more than %d processes can be spawned",
NGX_MAX_PROCESSES);
return NGX_INVALID_PID;
}
} if (respawn != NGX_PROCESS_DETACHED) { /* Solaris 9 still has no AF_LOCAL */
//创建socketpair用于进程间通信,master进程为每个worker创建一对socket
if (socketpair(AF_UNIX, SOCK_STREAM, , ngx_processes[s].channel) == -)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"socketpair() failed while spawning \"%s\"", name);
return NGX_INVALID_PID;
} ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, ,
"channel %d:%d",
ngx_processes[s].channel[],
ngx_processes[s].channel[]);
//设置非阻塞模式
if (ngx_nonblocking(ngx_processes[s].channel[]) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
if (ngx_nonblocking(ngx_processes[s].channel[]) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
//设置异步模式
on = ;
if (ioctl(ngx_processes[s].channel[], FIOASYNC, &on) == -) { //FIOASYNC异步输入/输出标志
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"ioctl(FIOASYNC) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} if (fcntl(ngx_processes[s].channel[], F_SETOWN, ngx_pid) == -) {//F_SETOWN设置异步I/O的所有者
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(F_SETOWN) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
//若进程执行了exec后,关闭socket
if (fcntl(ngx_processes[s].channel[], F_SETFD, FD_CLOEXEC) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} if (fcntl(ngx_processes[s].channel[], F_SETFD, FD_CLOEXEC) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} ngx_channel = ngx_processes[s].channel[]; } else {
ngx_processes[s].channel[] = -;
ngx_processes[s].channel[] = -;
} ngx_process_slot = s; //创建子进程
pid = fork(); switch (pid) { case -:
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 :
ngx_pid = ngx_getpid();
proc(cycle, data); //调用ngx_worker_process_cycle()子进程循环处理事件
break; default:
break;
} ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "start %s %P", name, pid); ngx_processes[s].pid = pid;
ngx_processes[s].exited = ;
//大于0,说明确定重启该进程
if (respawn >= ) {
return pid;
}
//设置进程信息
ngx_processes[s].proc = proc;
ngx_processes[s].data = data;
ngx_processes[s].name = name;
ngx_processes[s].exiting = ;
//设置状态信息
switch (respawn) { case NGX_PROCESS_NORESPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_JUST_SPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_RESPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_JUST_RESPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_DETACHED:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break;
} if (s == ngx_last_process) {
ngx_last_process++;
} return pid;
}
接下来,看看Nginx是如何在进程间进行通信的,ngx_pass_open_channel函数:
static void
ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)
{
ngx_int_t i;
//遍历所有进程
for (i = ; i < ngx_last_process; i++) {
//跳过本进程和退出/不能通信的进程
if (i == ngx_process_slot
|| ngx_processes[i].pid == -
|| ngx_processes[i].channel[] == -)
{
continue;
} ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, ,
"pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
ch->slot, ch->pid, ch->fd,
i, ngx_processes[i].pid,
ngx_processes[i].channel[]); /* TODO: NGX_AGAIN */
//把本进程的信息发送给每一个进程的父进程
ngx_write_channel(ngx_processes[i].channel[],
ch, sizeof(ngx_channel_t), cycle->log);
}
}
ngx_write_channel原型:
ngx_int_t
ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, //ch内储存着本进程的信息,s是父进程的socket值(channel[0])
ngx_log_t *log)
{
ssize_t n;
ngx_err_t err;
struct iovec iov[];
struct msghdr msg; #if (NGX_HAVE_MSGHDR_MSG_CONTROL) union {
struct cmsghdr cm;
char space[CMSG_SPACE(sizeof(int))];
} cmsg; if (ch->fd == -) {
msg.msg_control = NULL;
msg.msg_controllen = ; } else {
msg.msg_control = (caddr_t) &cmsg;
msg.msg_controllen = sizeof(cmsg); cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
cmsg.cm.cmsg_level = SOL_SOCKET;
cmsg.cm.cmsg_type = SCM_RIGHTS; /*
* We have to use ngx_memcpy() instead of simple
* *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;
* because some gcc 4.4 with -O2/3/s optimization issues the warning:
* dereferencing type-punned pointer will break strict-aliasing rules
*
* Fortunately, gcc with -O1 compiles this ngx_memcpy()
* in the same simple assignment as in the code above
*/ ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int));
} msg.msg_flags = ; #else if (ch->fd == -) {
msg.msg_accrights = NULL;
msg.msg_accrightslen = ; } else {
msg.msg_accrights = (caddr_t) &ch->fd;
msg.msg_accrightslen = sizeof(int);
} #endif iov[].iov_base = (char *) ch;
iov[].iov_len = size; msg.msg_name = NULL;
msg.msg_namelen = ;
msg.msg_iov = iov;
msg.msg_iovlen = ; n = sendmsg(s, &msg, );//sendmsg函数,在这里用于进程间通信 if (n == -) {
err = ngx_errno;
if (err == NGX_EAGAIN) {
return NGX_AGAIN;
} ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed");
return NGX_ERROR;
} return NGX_OK;
}
Nginx学习笔记(七) 创建子进程的更多相关文章
- 七、Nginx学习笔记七Nginx的Web缓存服务
user www; worker_processes 1; error_log /usr/local/nginx/logs/error.log crit; pid /usr/local/nginx/l ...
- (转)Qt Model/View 学习笔记 (七)——Delegate类
Qt Model/View 学习笔记 (七) Delegate 类 概念 与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件.一般来讲, view负责把数据展示 给用户,也 ...
- Nginx学习笔记4 源码分析
Nginx学习笔记(四) 源码分析 源码分析 在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem).那就来看看这个文件吧!从简单的开始~~~ src/os/unix/Ngx_ ...
- Typescript 学习笔记七:泛型
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Go语言学习笔记七: 函数
Go语言学习笔记七: 函数 Go语言有函数还有方法,神奇不.这有点像python了. 函数定义 func function_name( [parameter list] ) [return_types ...
- nginx 学习笔记(2) nginx新手入门
这篇手册简单介绍了nginx,并提供了一些可以操作的简单的工作.前提是nginx已经被安装到你的服务器上.如果没有安装,请阅读上篇:nginx 学习笔记(1) nginx安装.这篇手册主要内容:1. ...
- go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer)
目录 go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer) demo demo server demo client 池 dao service p2c ro ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- springmvc学习笔记---idea创建springmvc项目
前言: 真的是很久没搞java的web服务开发了, 最近一次搞还是读研的时候, 想来感慨万千. 英雄没落, Eclipse的盟主地位隐隐然有被IntelliJ IDEA超越的趋势. Spring从2. ...
- Learning ROS for Robotics Programming Second Edition学习笔记(七) indigo PCL xtion pro live
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...
随机推荐
- Linux_06------Linux的磁盘管理
进制编码 * 3.格式化交换分区 * 4.启用交换分区 * * 1.2. * fdisk /dev/sdb * p * t * 6(分区编号) * L(查看编码列表),找到SWAP交换分区标号 * 8 ...
- SQLServer 命令批量删除数据库中指定表(游标循环删除)
DECLARE @tablename VARCHAR(30),@sql VARCHAR(500)DECLARE cur_delete_table CURSOR READ_ONLY FORWARD_ON ...
- centos安装mysql5.6的正确姿态
1.准备工作 a)卸载centos默认软件 yum remove mariadb-libs-5.5.35-3.el7.x86_64 b)安装依赖包 yum install -y perl-Module ...
- js 获取 通过 ”?“ 或者 ”&“ url 传过来参数值
请先 引用 jQuery的js <script> String.prototype.GetValue=function(para) { var reg = new RegExp(" ...
- 使用Innosetup制作安装包的一些技巧
1. 选择安装界面上的图片 [Setup] ;设置界面上的两个图片 WizardImageFile=WizModernImage.bmp WizardSmallImageFile=WizSmallIm ...
- 线程池pool
参考链接 http://www.open-open.com/lib/view/open1415453575730.html 参考配置 http://www.cnblogs.com/linjiqin/a ...
- [原创]Spring MVC 学习 之 - URL参数传递
原文参考地址: http://www.cnblogs.com/rhythmK/p/3971191.html 目的和缘由: 本人想做一个分享的页面,分析给朋友注册,注册按分享ID进行级联; 过程: 很多 ...
- CMS为什么采用“标记-清除”算法
分代式GC里,年老代常用mark-sweep:或者是mark-sweep/mark-compact的混合方式,一般情况下用mark-sweep,统计估算碎片量达到一定程度时用mark-compact. ...
- Nodejs编码转化问题
目前Node.js仅支持hex.utf8.ascii.binary.base64.ucs2几种编码的转换.对于GBK,GB2312等编码,Nodejs自带的toString()方法不支持,因此中文转化 ...
- 【转】详解Python的装饰器
原文链接:http://python.jobbole.com/86717/ Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现 ...