event中将事件驱动模块,由于支持跨平台,抽象出了event模块。它支持的event类型有:
1、AIO(异步I/O)
2、/dev/poll(solaris和unix特有)
3、epoll(linux2.6特有)
4、eventport(solaris 10特有)
5、kqueue(BSD特有)
6、poll
7、rtsig(实时信号)
8、select
event模块的主要功能就是,监听accept后建立的连接,对读写事件进行添加删除。事件驱动模型和非阻塞I/O模型结合在一起使用。当I/O可读可写的时候,相应的读写事件就会被唤醒,此时会去处理事件的回调函数
对于Linux, nginx大部分event用epoll EPOLLET(边沿触发)的方法来唤醒事件,只有listen端口的读事件是EPOLLLT(水平触发),对于边沿触发,如果出现了可读事件,必须及时处理,否则可能会出现读事件不再触发而没有处理的情况。
event的结构体为:
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;
accept锁
当内核accpet一个连接时,会唤醒所有等待的进程,但实际上只有一个进程能获取连接,其它的进程都是无效唤醒的。
nginx的事件处理入口函数为ngx_process_events_and_timers(),其加锁过程为
if (ngx_use_accept_mutex) { if (ngx_accept_disabled > 0) { ngx_accept_disabled--; } else { if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { return; } if (ngx_accept_mutex_held) { flags |= NGX_POST_EVENTS; } else { if (timer == NGX_TIMER_INFINITE || timer > ngx_accept_mutex_delay) { timer = ngx_accept_mutex_delay; } } } }
ngx_process_events函数是所有事件处理的入口,它会遍历所有的事件。抢到accept锁的进程与一般进程有些区别,加上了NGX_POST_EVENTS标志表示ngx_process_events只是将事件放到post_events事件的队列中,而不处理。直到ngx_accept_mutex锁去掉后才去处理事件。为什么这样?因为这样做可以减小该进程抢到锁后,从accept开始到结束的时间,以便其它进程继续接收新的连接,提高吞吐量。
ngx_posted_accept_events和ngx_posted_events 分别是accept延迟事件队列和普通延迟事件队列。其实ngx_posted_accept_events还是放在ngx_accept_mutex锁里面处理的,该队列处理的都是accept事件,它会把内核backlog里等待的连接都accept进来,注册到读写事件中。