NGINX源码之:event与epoll

在进入正题之前,先来大概了解下epoll:
引入多路复用之前socket建立连接流程:
1、服务端先建立socket(serversocket)占用一个文件描述符fd,然后bind端口,开启监听listen accept事件;
2、客户端请求服务端,serversocket接收到accept事件,创建新的socket,用来监听read事件(建立连接)
3、开始通信

引入epoll之后,第一步跟上面的第一步一致,开启监听后还要完成三个操作:

1、epoll_create(int size);
在内核中创建一个epoll描述符efd,用于管理事件监听的socket fd;


2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);
向efd中注册socket fd需要监听的事件,serversocket对应accpet事件,连接socket则对应read事件。
op表示动作:用三个宏表示:
EPOLL_CTL_ADD:注冊新的fd到epfd中;
EPOLL_CTL_MOD:改动已经注冊的fd的监听事件,当某个fd已经注册了事件,只能修改不能新增,新增则报错;
EPOLL_CTL_DEL:从epfd中删除一个fd;


events能够是下面几个宏的集合
EPOLLIN :表示相应的文件描写叙述符能够读(包含对端SOCKET正常关闭);
EPOLLOUT:表示相应的文件描写叙述符能够写。
EPOLLPRI:表示相应的文件描写叙述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示相应的文件描写叙述符错误发生;
EPOLLHUP:表示相应的文件描写叙述符被挂断。
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式。这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:仅仅监听一次事件。当监听完这次事件之后,就会把这个fd从epoll的队列中删除。


3、int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
阻塞等待事件发生,返回须要处理的事件的数量,并将需处理事件的套接字集合于參数events内。參数timeout是超时时间(毫秒,0会马上返回(noblocking)。-1是永久堵塞)。该函数返回须要处理的事件数目。如返回0表示已超时。

此时,accept事件,read事件都由内核efd中管理以红黑树组织,内核触发(中断)后,应用通过调用epoll_wait获取触发的事件。
这里有张以前学习nio时的笔记图:(原图是马士兵啥课程的,我在原图上做了笔记,马士兵虽然。。但里面还是有牛逼的老师的)
在这里插入图片描述
接下来进入正题,来看看Nginx的event处理机制与epoll的协同:

一、event_block解析、完成配置
先来看下关于event_module的城市化操作:
首先在ngx_events_block()中调用ngx_event_core_create_conf()给ecf->use赋初始值-1
在这里插入图片描述
解析event_block时,如果配置了use,则使用use 指定的模块作为event事件处理模块给ecf->use赋值use指向的就是event处理的具体模块。ngx_event_use()
在这里插入图片描述
但如果没有配置use呢,那就使用默认的:
在ngx_events_block()方法的最后,调用ngx_event_core_init_conf给ecf->use赋默认值(ngx_epoll_module模块名:epoll)
在ngx_init_cycle()方法中,完成所有创建监听后会遍历所有的module完成init操作在这里插入图片描述
遍历中会执行ngx_event_module_init();这个方法主要是判断连接数设置会不会超过系统硬链接句柄限制,与共享内存设置。共享内存在Nginx中主要用于多进程交互,暂时先不看,后面解读Nginx多进程时,再回来看这个方法。

二、ngx_event_process_init() event模块核心处理
Nginx有多进程处理和单进程处理模式,但是再event处理的机制上都是一样的,下面以单进程处理为突破口解读:ngx_single_process_cycle()
在这里插入图片描述
该方法首先完成每个模块的init_process(),然而默认的所有模块中,只有ngx_event_core_module有设置该方法:ngx_event_process_init()

1、首先在ngx_event_process_init获取use对应的模块,完成module->action.init
在这里插入图片描述

在这里插入图片描述
ngx_epoll_init()代码片段如下:
在这里插入图片描述
2、ngx_event_process_init()创建连接与accept事件监听
在这里插入图片描述
结构图如下:在这里插入图片描述

3、注册监听事件ngx_epoll_add_event()
在这里插入图片描述
注册完accept事件后,就等着客户端来连接了

三、开启等待获取监听事件并处理ngx_process_events_and_timers()
在这里插入图片描述
处理事件ngx_epoll_process_events()
在这里插入图片描述
1、accept事件处理
从前面的流程可以得知,TCP连接accept事件的处理器为:ngx_event_accept()
accept事件本质上也是read,但是其对应的是绑定ip端口,开启监听的socket(fd)。其主要作用就是接受请求,创建连接,为连接对应的fd绑定读事件处理handler。
在这里插入图片描述
在ngx_http_init_connection中,会对当前请求连接对象设置对应的srv_conf配置,后续将通过该srv_conf配置,查找location配置。在这里插入图片描述

因此ngx_event_accept中,创建了新socket绑定新的connection,该socket绑定对应读事件处理handler:ngx_http_wait_request_handler专门处理请求数据,并注册新socket fd即对应监听事件到efd。到此总的结构图如下:
在这里插入图片描述
至此,当新socket fd发生写事件时,ngx_process_events_and_timers中将调用ngx_http_wait_request_handler完成http请求处理。

2、补充说明连接处理:
在ngx_event_process_init()中完成了free_connections的初始化;
再通过其他位置ngx_get_connection()获取连接结构体,从上面可知,当空闲连接队列有富余时,直接获取空闲队列第一个元素,并将空闲队列指向下一个元素。但当空闲队列不足时,则需要通过调用ngx_drain_connections()方法,释放可复用的连接:
在这里插入图片描述
总的来说,当某个连接被被某个连接socket fd绑定,刚绑定时,设置ngx_reusable_connection(c, 1);设置为可复用,并放入可复用连接队列。当空闲连接不足时,会从可复用连接队列剔除一部分,添加到空闲队列。但是,当绑定的连接正在使用时,会设置为ngx_reusable_connection(c, 0);从可复用队列剔除,等待使用完成后再close,因此从可复用队列释放的连接,不会是正在使用中的连接,等某次请求使用完成后,调用ngx_http_finalize_request()–>ngx_http_finalize_connection()->ngx_http_close_request()->ngx_http_close_connection()释放连接,放回空闲连接队列,同时,会关闭绑定的socket fd。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值