从 mian 函数开始一步一步分析 nginx 执行流程(三)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件!
这一节我们分析ngx_start_worker_processes(),该函数代码比较少,因为它通过调用函数实现功能的,先贴出代码:
[os/unix/ngx_process_cycle.c]
- /* 这是 master 线程调用的、用来生成 worker 进程的入口 */
- 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;
- /* n 代表要创建多少个 worker 进程 --- 由配置文件中的 worker_process 的值决定的 */
- for (i = ; i < n; i++) {
- /* 多 CPU 获取 CPU 的信息 */
- cpu_affinity = ngx_get_cpu_affinity(i);
- /* 创建进程函数 */
- /* ngx_spawn_process() 在 OS/ngx_process.h/c 文件中定义 ,ngx_worker_process_cycle() 是新建进程要执行的函数 */
- 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[];
- /* 用来干嘛? */
- ngx_pass_open_channel(cycle, &ch);
- }
- }
1. 我们可以看到,该函数接受三个参数,分别是全局变量 cycle,要创建 worker 进程的个数 n, 要创建进程的类型 type,type有以下 5 个值。
a. NGX_PROCESS_RESPAWN
b. NGX_PROCESS_NORESPAWN
c. NGX_PROCESS_JUST_SPAWN
d. NGX_PROCESS_JUST_RESPAWN
e. NGX_PROCESS_DETACHED
type 的值将影响进程结构体 ngx_process_t 的 respawn、detached、just_spawn 标志位的值。
2. 386 行设置 ngx_channerl_t 结构体 command 的变量为 NGX_CMD_OPEN_CHANNEL,暂时还没分析到这个 channel,我猜测是意味着打开 channel,开始进行父子进程间的通信了。
3. 389-404 行通过 for 循环创建 n 个 worker 进程,其中最终要的就是 ngx_spawn_process() 函数了,该函数真正用来创建进程。我慢来看一些 ngx_spawn_process() 的代码:
[os/unix/ngx_process.c]
- 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;
- } 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 */
- /* 创建一对 socket 描述符存放在变量 ngx_process[s].channel 中*/
- 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[]);
- /* 这里将 channel[0],channel[1] 看着是管道的两端 */
- /* 将 channel[0] 设置为非阻塞 */
- 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;
- }
- /* 将 channel[1] 设置为非阻塞 */
- 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) == -) {
- 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;
- }
- /* F_SETOWN 设置接受 SIGIO 和 SIGURG 信号的进程 ID 或 进程组 ID */
- if (fcntl(ngx_processes[s].channel[], F_SETOWN, ngx_pid) == -) {
- 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;
- }
- 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);
- 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 = ;
- 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;
- }
ngx_spawn_process()函数代码
3.1. 该函数代码太长,我将它折叠!接下来我们一步步分析。先看96-114行代码。如果 respawn(是否是重新生成子进程)大于等于 0(如果大于等于 0 ,那说明 type 不是上面说到的 5 中类型之一,因为上面个的 5 个类型的宏定义全是负值见[os/unix/ngx_process.h]。此时的 respawn 应该是作为要重新被生成的进程在 ngx_process 数组中的下标),那么 s 就等于 respawn, s 是即将要被创建的进程在进程数组 ngx_process 中的下标。如果 respawn 小于 0,那么就要在 ngx_process 数组中找到一个可用的空间,用来保存即将要被创建的进程的信息。 108-113 行表明创建的进程数已经达到设置的能创建的最大进程数---NGX_MAX_PROCESS,那么 ngx_spawn_process 创建进程失败。
3.2. 如果 raspawn 类型是 NGX_PROCESS_DETACHED 那么意味着创建的进程和 master 进程没有父子关系,则设置该进程的 channel[0] 和 channel[1] 的值为 -1(相当于管道的两端都关闭),即子进程不与父进程进行通信。如果是 NGX_PROCESS_DETACHED 意外的其他类型,120 行通过调用 socketpair() 函数创建用于父子间通信的匿名已经连接的套接字(理解为匿名管道也没错哈),如果失败,则返回错误!
3.3. 134-182 都是在设置创建的套接字两端的属性。134 行和 143 行分别将套接字两端 (channel[0] 和 channel[1]表示) 设置为 non-blocking 。152 行通过 ioctl 函数设置针对 channle[0] 端信号异步 I/O 标志(它决定是否收取针对本套接口的异步 I/O 信号(SIGIO))。这里设置为接受针对该套接口(channel[0])的异步I/O信号(SIGIO)。160 行设置 channel[0] 端接收 SIGIO 和 SIGURG 信号的进程 ID 或进程组ID,这里设置的进程 ID 是 nginx 的 master 进程。 167 和 175 行设置 channel[0] 端和 channel[1] 端的描述符标志为 FD_CLOSEXEC。
3.4. 191 将 s 复制给 ngx_process_slot。194 调用 fork() 创建进程。如果创建失败,ngx_spawn_process 结束,返回 ngx_invalid_pid 错误
3.5. 205 如果是子进程(新创建的 wroker 进程),则调用 pro 函数,注意:该函数是通过 ngx_spawn_process() 函数传过来的变量 proc,他是一个函数指针,该变量值是 ngx_worker_process_cycle [os/unix/ngx_process_cycle.c]是一个函数,是 worker 进程要执行的函数。这个函数是 worker 进程的重点。我们下一节会分析该函数。
3.6. 显然 215-该函数结束都是父进程继续执行的代码( worker 进程已经进入 proc 函数运行了)。224-260 都是保存新创建的进程信息到 ngx_process[i] 。
3.7. 262-264 实现ngx_last_process 的自增。到这里,ngx_spawn_process() 函数执行完毕了!
4. 398-400 保存子进程的 pid ,进程在 ngx_prcess 数组中的下标和子进程的 channel[0] 端( )到 ch。
5. 403行通过调用 ngx_pass_open_channel() 传递与新创建进程的 ch 到其他 worker 进程。---待分析,可能 worker 之间也要进行通信!
6. 至此ngx_process_cycle 函数分析完毕,下一节我们将分析 ngx_worker_process_cycle() 函数---一个 worker 进程真正执行的代码函数。
从 mian 函数开始一步一步分析 nginx 执行流程(三)的更多相关文章
- 从 mian 函数开始一步一步分析 nginx 执行流程(二)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件! 上一个博客中我们将 main 函数执行流程分析完,到最后一步调用 ngx_master_process ...
- 从 mian 函数开始一步一步分析 nginx 执行流程(四)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件! 这一节我们分析ngx_worker_process_cycle(),该函数代码比较少,因为它通过调用函 ...
- 从 mian 函数开始一步一步分析 nginx 执行流程(一)
如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件! 我们先贴出 main 函数的部分代码: [core/nginx.c] int ngx_cdecl ma ...
- javascript 函数 add(1)(2)(3)(4)实现无限极累加 —— 一步一步原理解析
问题:我们有一个需求,用js 实现一个无限极累加的函数, 形如 add(1) //=> 1; add(1)(2) //=> 2; add(1)(2)(3) //=> 6; add ...
- 一步一步开发Game服务器(四)地图线程
时隔这么久 才再一次的回归正题继续讲解游戏服务器开发. 开始讲解前有一个问题需要修正.之前讲的线程和定时器线程的时候是分开的. 但是真正地图线程与之前的线程模型是有区别的. 为什么会有区别呢?一个地图 ...
- 一步一步学ROP之linux_x64篇
一步一步学ROP之linux_x64篇 一.序 **ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防 ...
- 一步一步学ROP之linux_x86篇
一步一步学ROP之linux_x86篇 作者:蒸米@阿里聚安全 一.序 ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过 ...
- 大流量网站性能优化:一步一步打造一个适合自己的BigRender插件
BigRender 当一个网站越来越庞大,加载速度越来越慢的时候,开发者们不得不对其进行优化,谁愿意访问一个需要等待 10 秒,20 秒才能出现的网页呢? 常见的也是相对简单易行的一个优化方案是 图片 ...
- 使用Python一步一步地来进行数据分析总结
原文链接:Step by step approach to perform data analysis using Python译文链接:使用Python一步一步地来进行数据分析--By Michae ...
随机推荐
- app抓包
http://www.360doc.com/content/14/1126/11/9200790_428168701.shtml 记得下载证书 不然有些网站是抓不到的
- char nvarchar varchar
1. char 固定长度,最长n个字符. 2. varchar 最大长度为n的可变字符串. (n为某一整数,不同数据库,最大长度n不同) char和varchar区别: ...
- 服務器提交協議衝突 (The server committed a protocol violation.)
---解決方法 (放在 app.config / web.config)--- <system.net> <settings> <httpWebRequest useUn ...
- PHP获取用户访问IP地址的5种方法
IP地址获得的五种方法: <?php //方法1: $ip = $_SERVER["REMOTE_ADDR"]; echo $ip; //方法2: $user_IP = ($ ...
- 初学HTML5系列二:HTML5新增的事件属性
Window事件属性: 属性 值 描述 onafterprint script 文档打印之后运行的脚本. 属性发生于用户设置页面打印并且打印对话框已出现之后. onbeforeprint scri ...
- [DB2]实现项目多数据库切换(上)--环境部署
基本软硬件信息:Windows 8.1 X64 / Microsoft Visual Studio 2012 / ThinkPad S3-S431 安装工具:IBM Data Studio 4.1. ...
- iOS 使用GBK编码的hmacMD5算法
该方法是写在工具类中的,而不是写在NSString的类别中 方法的声明: /** * 使用hmac-md5加密 * * @param clearText 原文 * @param secret ...
- php文件锁(转)
bool flock ( int handle, int operation [, int &wouldblock] );flock() 操作的 handle 必须是一个已经打开的文件指针.o ...
- Photon开发实战(2)——开发框架、第一个Photon程序
Photon基础开发框架 Photon (v4)的基本框架.开发框架主要Photon和游戏逻辑(C#)两个部分,如下图最新的Photon v4支持的4种底层协议,游戏开发逻辑Photon目前主要划分为 ...
- IE9透明filter和opacity同时生效的解决办法 IE9 hack only
转载请注明:来自于http://www.cnblogs.com/bluers/ 问题: 假设结构如下: <div class="wrapper"> <p clas ...