January 12th Tuesday 2010
Nginx(八) 工作进程
ngx_event_find_timer()函数。
nginx中的timer用红黑树的结构排序。ngx_event_timer_rbtree就是nginx中timer的红黑树。Ngx_event_timer_rbtree的结构如下:
typedef struct ngx_rbtree_s ngx_rbtree_t;
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
struct ngx_rbtree_s {
ngx_rbtree_node_t *root;
ngx_rbtree_node_t *sentinel;
ngx_rbtree_insert_pt insert;
};
树中节点的结构:
typedef ngx_uint_t ngx_rbtree_key_t;
typedef ngx_int_t ngx_rbtree_key_int_t;
typedef struct ngx_rbtree_node_s ngx_rbtree_node_t;
struct ngx_rbtree_node_s {
ngx_rbtree_key_t key;
ngx_rbtree_node_t *left;
ngx_rbtree_node_t *right;
ngx_rbtree_node_t *parent;
u_char color;
u_char data;
};
ngx_event_find_timer()逻辑如下:
1. ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel时,返回NGX_TIMER_INFINITE(即-1);
2.否则,对ngx_event_timer_mutex上锁;
3.调用ngx_rbtree_min(root, sentinel)取得最小值的timer节点;
4.解锁ngx_event_timer_mutex;
5.用最小值的timer节点的值减去ngx_current_msec值;
6.步骤5中计算的值大于0时,返回这个值,否则返回0。
Nginx里面,当前所有可能被触发的定时器被保存在红黑树这种数据结构中,通过红黑树,你可以很快的得到距离当前最快发生的定时器事件的时间差。将这个时间差作为select/poll/epoll等函数的参数,也就是,最多等待这么长时间就返回。当函数返回时,得到函数调用总共花费了多少时间,根据这个时间取出红黑树的根节点比较查看是否应该触发该定时器时间,如果可以则将该定时器从红黑树中删除,然后继续查看新的成为树根的定时器节点,这个过程一直进行下去直到没有定时器满足被触发的条件,也就是还没有到被触发的事件。
Nginx里面,新接收了一个连接,会保存这个连接上来的时间,并且以这个时间来加入红黑树定时器中。
可以看到,因为引入了红黑树这个数据结构,所有的定时器都可以按照顺序来依次取出,这样不用轮询所有事件来查看是否超时了;而以距离当前最快发生的定时器事件时间差作为轮询的定时,又可以不用使用alarm信号来触发定时,一举两得。
ngx_process_events_and_timers()函数。
1. ngx_timer_resolution不为0时,timer值设为NGX_TIMER_INFINITE,flag为0;
否则,使用ngx_event_find_timer()去计算timer值,flags为NGX_UPDATE_TIME。
如果线程支持时,timer == NGX_TIMER_INFINITE || timer > 500情况下,timer设为500。
(注:ngx_timer_resolution值来自配置文件中的timer_resolution项。
语法: timer_resolution t
缺省值: none Example: timer_resolution 100ms; The directive allows to decrease number gettimeofday() syscalls.By缺省值 gettimeofday() is called after each return from kevent(), epoll, /dev/poll, select(), poll(). But if you need an exact time in logs when logging $upstream_response_time, or $msec variables, then you should use timer_resolution。)
2. ngx_use_accept_mutex为1时,
a) ngx_accept_disabled > 0时,ngx_accept_disabled--;
b)否则调用ngx_trylock_accept_mutex()试着给cycle上锁;
c)上锁成功,如果ngx_accept_mutex_held不为0,flags |= NGX_POST_EVENTS;
否则查看timer是否是NGX_TIMER_INFINITE或者timer > ngx_accept_mutex_delay,为真时,ngx_accept_mutex_delay设为timer;
d) 调用ngx_process_events();(ngx_process_events是个宏,实际是ngx_event_actions.process_events。在不同系统中具体调用不同的函数。)
注:accept_mutex这个值来自哪能里?有什么用?下面是从wiki.nginx.org网站上找到的。
accept_mutex
Syntax: accept_mutex [ on | off ]
Default: on
nginx uses accept mutex to serialize accept() syscalls.
accept_mutex_delay
Syntax: accept_mutex_delay Nms;
Default: 500ms
If a worker process does not have accept mutex it will try to aquire it at least after this delay. By default delay is 500ms.
先看ngx_process_events这个宏,明显它才是关键部分。
ngx_process_events():
1. 找到它有定义 #define ngx_process_events ngx_event_actions.process_events;
2. 继续找ngx_event_actions,它的结构体如下定义:
typedef struct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*add_conn)(ngx_connection_t *c);
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
1. 在nginx源代码中搜索ngx_event_action关键字。发现如下结果:
Ngx_aio_module.c (src/event/modules): ngx_event_actions = ngx_aio_module_ctx.actions;
Ngx_devpoll_module.c (src/event/modules): ngx_event_actions = ngx_devpoll_module_ctx.actions;
Ngx_epoll_module.c (src/event/modules): ngx_event_actions = ngx_epoll_module_ctx.actions;
Ngx_event.c (src/event):ngx_event_actions_t ngx_event_actions;
…
前面三行表示,不同event模块对象中的actions就是ngx_event_ations_t对象,而ngx_event_actions在第四行定义成一个全局变量,明显是用来作统一接口用的。那么,就一个event模块看看具体它负责什么工作。就epoll了。
2. 先找到ngx_event_module_t的结构体。
typedef struct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;
name就不说了,当然是模块名称了。create_conf钩子调函数主要是创建一块内存用来保存配置信息;init_conf钩子函数当然是初始化用的。重要的是actions成员。看epoll模块的上下文变量定义。
ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
{
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
NULL, /* process the changes */
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};
难得有点注释呀!
ngx_epoll_process_events()这个函数就是我们要找的,调用ngx_process_events()实际上就是调用它(我是指linux环境下且支持epoll时)。累呀,找个函数都这么繁烦!
3. ngx_epoll_process_events()函数。这个函数大约150多行。一开始就调用epoll_wait()。终于见到亲人了。
events = epoll_wait(ep, event_list, (int) nevents, timer);
7.ngx_epoll_process_events()函数的主要逻辑。
epoll_wait();
检查有没有出错;
If 有出错 then
出错处理;
End if
If 没有任何事件发生 then
If timer不是无限超时 then
返回ok;
End if
返回Error;
End if
ngx_posted_events_mutex上锁;(ngx_posted_events_mutex只有在NGX_THREAD宏定义有效时才有效。)
foreach 发生的epoll事件对象 do
从发生的epoll事件对象中取得ngx_connection_t对象;
ngx_connection_t对象中取出读事件;
一些查错工作;
If 读事件且ready状态 then
If (flags & NGX_POST_THREAD_EVENTS) && !rev->accept then
该读事件设为posted_ready状态;
Else
该读事件设为ready状态;
End if
If flags & NGX_POST_EVENTS then
/* 表示不立即处理,要事件排队. */
If 该读事件为accept状态 then
将其放入ngx_posted_accept_events事件队列中;
Else
将其放入ngx_posted_events事件队列中;
End if
Else
调用这个读事件的处理函数;(通常就是读取数据了)
End if
End if
ngx_connection_t对象中取出写事件;
If 写事件且ready状态 then
If flags & NGX_POST_THREAD_EVENTS then
该写事件设为posted_ready状态;
Else
该写事件设为ready状态;
End if
If flags & NGX_POST_EVENTS then
/* 表示不立即处理,要事件排队. */
将其放入ngx_posted_events事件队列中;
Else
调用这个写事件的处理函数;(通常就是写入数据了)
End if
End if
End foreach;
ngx_posted_events_mutex解锁;
其中用到了ngx_locked_post_event()这个宏,它把事件放到事件队列的头部。
January 12th Tuesday 2010相关推荐
- January 7th Tuesday 2010
Nginx(六) 1. ngx_start_worker_processes()函数,这个函数按指定数目n,以ngx_worker_process_cycle()函数为参数调用ngx_spawn_pr ...
- January 14th Tuesday 2010
Nginx (九) 事件 结构 struct ngx_event_s { void *data; unsigned write:1; unsigned ...
- January 11th Monday 2010
Nginx(七) 工作进程 进程部份 一切从ngx_worker_process_init()函数开始: 1.先调用ngx_set_environment()函数为本进程设定环境变量,那些环境变量都 ...
- English: date time
year 写法方面:直接写数字,如 1993. 朗读方面:将表示年份的四个数字按前后分为两组,每一组的数字都按基数词来读.如:1993 读成 nineteen ninety-three. 几种特殊情况 ...
- How to recognise a good programmer
导读: Tuesday, November 13th, 2007...1:28 pm Jump to Comments How do you recognise good programmers if ...
- 英语发音表及读法_在英语中年月日的读法和写法(附:英美时间表达差异辨析)...
"本文来源于网络,版权归原作者所有,侵权删" 1.年份关于四位数年份的读法有下列几种情形:(1) 一般情况下,将表示年份的四个数字按前后分为两组,每一组的数字都按基数词来读.186 ...
- centos 6.4 postfix mysql_CentOS 6.4下Postfix邮件服务安装和基本配置
三.基于Postfix构建简单电子邮件 1.配置并测试Postfix服务器 1>.编辑main.cf文件,调整Postfix的基本运行参数 [root@mail~]# vi /etc/postf ...
- ENGLISH资料收集(3)-英语日期的正确表达
1.年份 关于四位数年份的读法有下列几种情形: (1) 一般情况下,将表示年份的四个数字按前后分为两组,每一组的数字都按基数词来读. 1865年读作 eighteen sixty-five 1998年 ...
- 高并发 高负载 网站系统架构
高并发 高负载 网站系统架构 注:我看到这篇文章写的太好了,可以没法转到CSDN上我就COPY了,看到下面激烈的评论,我也一并COPY了.不过还是要谢谢哪位作者了.这样的文章很少. 转自:http:/ ...
最新文章
- keras,在 fit 和 evaluate 中 都有 verbose 这个参数标记是否打印进度条
- UVA - 1252 Twenty Questions (状压dp+vis数组加速)
- (转载)Google Analytics(Google分析)使用技巧
- javascript +new Date()
- 清洁代码_清洁单元测试
- 利用计算机可以对物体的运动情况,2018-2019学年高中物理第05章曲线运动专题5.3实验:研究平抛运动情景分组训练新人教版必修2.docx...
- 视频跟踪——meanshift算法
- 【Xamarin挖墙脚系列:关闭 OS X El Capitan 中 SIP 安全设置功能】
- 手写邮箱获取验证码注册登录功能
- 运行jar中某个类的main方法
- 万恶的单线程!!怎样才能实现一个真正的多线程的php socket server啊!!!
- OCR+NLP 提取信息并分析,这个开源项目火了!
- 有关C++的标准模板库(STL)的一些个人易错点
- 人脸对齐--采用dlib库的68_face_landmark进行人脸对齐操作
- 揭秘|Axway API在银行业的应用
- VS code 创建html文件后 !失效的解决
- 大白话搞懂什么是同步/异步/阻塞/非阻塞
- 计算机word考试试题模板,2017年职称计算机考试Word2003巩固练习题13
- iOS 指南针的制作 附带源码
- Linux中ssh基于密匙的安全验证过程是怎样的?
热门文章
- appium使用教程python_Appium使用教程
- 推荐几种方法把两个pdf合并成一个pdf
- 获取iframe src中参数
- mysql开通远程连接不上去_MySQL本地可以连接,远程连接不上的问题_夜风的BLOG-CSDN博客_mysql远程连接不上...
- I/O软件层次结构、I/O核心子系统以及假脱机技术(SPOOLing技术)
- Battery Life检测
- 【DP基础】晴天小猪历险记
- java shell spool_批量快速的导入导出Oracle的数据(spool缓冲池、java实现)
- 机器学习算法之SVM(二)概述
- 关于苹果内购问题游客登录的解决过程