基于muduo库编写的服务器运行时文件描述符的创建顺序分析

    这里仅以单IO线程为例,会了单IO线程的,其实多IO线程的也很好分析。

首先来看TcpServer类的数据成员:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第1张图片

TcpServer类的构造函数:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第2张图片

       研究TcpServer的数据成员与构造函数可以清晰的看到各个文件描述符的创建顺序。这有助于我们对整个服务器运行的流程有着一个较为清晰的了解。首先我们以TcpServer工作在单线程为例。众所周知,0、1、2分别对应stdin、stdout、stderr在每个进程创建时就有。我们来看一下TcpServer的构造函数,其首先会初始化loop_指针成员(其没有通过new的方式,在构造的时候就要求指针不为NULL),因此在调用TcpServer的构造函数之前,我们必然是已经构造出了一个EventLoop对象,然后将指向该EventLoop对象的指针传递给loop_成员,类似这样的构造过程。

EventLoop loop;
loop_(&loop);

       也就是说在此之前,我们必然构造出了一个EventLoop对象,因此我们先来看看EventLoop对象有没有创建描述符。来看看EventLoop的数据成员和构造函数。

EventLoop的数据成员:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第3张图片

EventLoop的构造函数:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第4张图片

        注意到EventLoop的构造函数内部构造了一个Poller对象,用成员指针poller_指向这个Poller对象,我们知道,默认的Poller对象是EPollPoller。EPollPoller的构造函数会调用epoll_create1函数创建一个红黑树,返回一个epollfd_,所以红黑树的根节点对应的描述符epollfd3.

EpollPoller的构造函数:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第5张图片

继续回到EventLoop的构造函数,我们看到,接下来EventLoop初始化了timerQueue_指针成员,即构造了TimerQueue类对象,

TimerQueue的数据成员:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第6张图片

TimerQueue的构造函数:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第7张图片

        注意到TimerQueue类里含有一个文件描述符timerfd_,其在TimerQueue类的构造函数中通过createTimerfd函数(底层调用了timerfd_create)创建了该描述符,所以timerfd对应的文件描述符为4.

       再回到EventLoop的构造函数,继续往下看,我们看到,接下来EventLoop的构造函数调用了createEventfd函数(底层调用了eventfd)创建了wakeupfd,所以wakeupfd对应的文件描述符为5.

       至此,EventLoop中的文件描述符都创建完了。我们来简单总结一下,EventLoop里共创建了3个文件描述符,按顺序分别为红黑树根节点对应的epollfd、TimerQueue里的描述符成员timerfd_和EventLoop类里的成员wakeupFd_。

       好了,回到一开始的TcpServer,我们现在讲完了loop_。接下来看看acceptor_成员,因为要初始化acceptor_指针成员,所以要构造一个Acceptor类对象。

Acceptor的数据成员:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第8张图片

Acceptor的构造函数:

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第9张图片

       注意看Acceptor的构造函数,其初始化了acceptSocket_指针,调用了 createNonblockingOrDie函数(底层调用了socket)创建了一个监听套接字listenfd,所以listenfd对应的文件描述符为6.acceptSocket_是Socket类的指针,我们知道Socket类本身就是对一个文件描述符的封装,一个Socket类本身就应该对应有一个文件描述符。

        这还没完,Acceptor类还有一个文件描述符,即idleFd_,其主要是为了防止服务器连接的客户端过多导致服务器来不及处理而客户端处对应的文件描述符会因服务器来不及处理而一直触发而设计的。所以idlefd对应的文件描述符为7.

       当我们执行Acceptor的listen函数,即正式地将把监听事件挂载到红黑树上,那么系统就可以正式地接受连接请求了。此时,如果有客户端发起连接请求,则TcpServer所包含的一个EventLoop(即成员指针loop_指向的EventLoop,也即Acceptor所在的EventLoop,即mainReactor)会处理连接请求。当然前提是我们执行了EventLoop的loop成员函数,开始事件循环监听(内部执行了通过面向对象的方式调用了虚函数,最终调用了epoll_wait,这样就能感知文件描述符状态变化)。

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第10张图片

        这时,其会执行在Acceptor构造函数中绑定的handleRead函数。

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第11张图片

       我们来看看Acceptor的handleRead函数,其内部调用了accept函数,会返回connfd,即已连接描述符,故而有多少连接描述符会一直增加,即connfd对应8、9、10......

基于muduo库编写的服务器运行时文件描述符的创建顺序分析_第12张图片

 

 

 

你可能感兴趣的:(muduo)