【Muduo源码剖析笔记】 网络库之EventLoop、Channel
EventLoop
声明了三个类,Channel、Poller和TimerQueue。
定义了Eventloop类。每个子线程拥有一个线程特有数据t_loopInThisThread,这是个Evenloop类型指针,子线程会在堆栈中创建new出自己对应的Eventloop对象,通过t_loopInThisThread这个指针访问和操作。
数据成员如下:
typedef std::function<void()> Functor;
typedef std::vector<Channel*> ChannelList;
bool looping_; /* atomic */
std::atomic<bool> quit_;
bool eventHandling_; /* atomic */
bool callingPendingFunctors_; /* atomic */
int64_t iteration_;
const pid_t threadId_;
Timestamp pollReturnTime_;
std::unique_ptr<Poller> poller_;
std::unique_ptr<TimerQueue> timerQueue_;
int wakeupFd_;
// unlike in TimerQueue, which is an internal class,
// we don't expose Channel to client.
std::unique_ptr<Channel> wakeupChannel_;
boost::any context_;
// scratch variables
ChannelList activeChannels_;
Channel* currentActiveChannel_;
mutable MutexLock mutex_;
std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
定义了一个全局变量 __thread EventLoop* t_loopInThisThread = 0。每个线程都有这么一个Evenloop类型指针。
int createEventfd()
全局函数,创建一个非阻塞的文件,返回该文件描述符。
Class IgnoreSigPipe
对一个对端已经关闭的socket调用两次write, 第二次将会生成SIGPIPE信号, 该信号默认结束进程
定义了一个类,在namespace的命名空间,初始化函数会让遇到SIGPIPE信号的时候,执行SIG_IGN函数。
EventLoop::EventLoop()
将looping_置为false,quit_z置为false,eventHandling_置为false,iteration_初始化为0。
threadId_(CurrentThread::tid()),线程id会用CurrentThread中的tid函数返回。也就是这个构造函数会被子线程调用,应该是使用new。
poller_(Poller::newDefaultPoller(this)):
timerQueue_(new TimerQueue(this)):new一个新的指针初始化一个unique_ptr指针。
wakeupFd_(createEventfd()):创建一个新的文件描述符。
wakeupChannel_(new Channel(this, wakeupFd_)):会用刚刚创建新的文件描述符,初始化一个指向Channel类型的unique_ptr。
currentActiveChannel_(NULL):Channel类型的指针,置为NULL。
函数中会将EventLoop创建的信息写进LOG中(DEBUG级别)。
如果创建EventLoop这个线程是已经有t_loopInThisThread这个指针指向的数据的话,那么就会报LOG_FATAL级别的错误。
不然就将t_loopInThisThread指针指向现在正在创建的Eventloop。
给wakeupchannel设定回调函数,这个回调函数读wakeupFd_,获取一个文件描述符。
然后通过wakeupChannel_开启Reading模式。
~EventLoop()
会输出哪个线程的Eventloop在哪个线程被销毁掉。
调用wakeupChannel的disableAll()函数,然后remove()。
之后把wakeupFd_文件描述符关闭。
把自己线程的loop置为NULL。(??如果关闭的是别人的loop呢)
EventLoop::loop()
首先确保Eventloop不是True(也就是这个loop没有启动),然后需要确认运行这个loop的线程是创建这个Eventloop的线程。然后把quit_置为false。
对LOG写入这个Eventloop要开始启动了的信息。
进入while(!quit_)循环。
首先将activeChannels_清空,activeChannels_是一个装有Channel指针的vector。
然后调用poller_->poll(kPollTimeMs, &activeChannels_),然后对iteration自加。
把eventHadnling设置为true;
然后使用for each的语法把channel从activeChannels中拿出来,置给currentActiveChannel_,然后调用Channel的
currentActiveChannel_->handleEvent(pollReturnTime_)。对这个对象的发生的事件进行处理。
作完后就把当前活跃管道设置为NULL,eventhandling也会设置为false。调用doPendingFunctors。
EventLoop::doPendingFunctors()
会调用pendingFunctors_中的函数。
void assertInLoopThread()
确保运行这个loop的线程是创建这个loop的线程。
EventLoop::updateChannel(Channel* channel)
首先确保这个Channel的主人是这个loop的,然后确认现在运行的loop是本人,之后调用poller_的updateChannel(channel)。
EventLoop::removeChannel(Channel* channel)
Channel会调用这个函数,把自己作为this指针传入,如果eventHandling_为真,那么会确认当前活跃的Channel为channel或者在列表。
调用poller_ 除掉removeChannle。
void EventLoop::quit()
把quit_标识设为true,如果调用这个loop的人不是创建这个loop的人
会调用wakeup()。
void EventLoop::wakeup()
wakeup会往wakeupfd中写入一个1。
Channel
定义了Channel类
封装了一个fd_文件描述符对应的事件,相应要发生什么事件需要处理时的操作函数等等。具体的调用由Eventloop调用。
typedef std::function<void()> EventCallback;
//事件回调函数为void()的函数对象
typedef std::function<void(Timestamp)> ReadEventCallback;
//写时间的回调函数?
数据成员:
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop* loop_; //channel所属的loop
const int fd_; //channel负责的文件描述符
int events_; //注册的事件
int revents_; //poller设置的就绪的事件
int index_; // //被poller使用的下标
bool logHup_;
std::weak_ptr<void> tie_;
bool tied_;
bool eventHandling_;
bool addedToLoop_;
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
loop_表示这个Channel隶属于哪个EventLoop,fd_表示这个Channel的对象是哪个文件描述符。然后保存了发生写读等事件的回调函数。
Channel(EventLoop* loop, int fd__)
接受一个loop指针和文件描述符,用loop初始化loop_和fd_初始化fd_。事件数量为0。
Channel::~Channel()
void Channel::tie(const std::shared_ptr& obj)
接受一个指向void的指针引用obj,把引用给tie_。并且设置tied_标志位为true。表示这个弱指针弱绑定了某个对象。
void Channel::update()
addedToLoop_会被置为真,然后调用loop_的updateChannel函数。updateChannel函数就会把调用poller_的updateChannel。也就是会把这个Channel加入到Eventloop中。
void Channel::remove()
确认events_ == kNoneEvent,然后将addedToloop的标识置为否,调用loop_的removeChannel把自己取出。
void Channel::handleEvent(Timestamp receiveTime)
创建一个局部的的shared_ptr 指针,如果tied_设置为true,就会调用tie_绑定的对象返回一个shared_ptr对象指针,然后调用handleEventWithGuard。
这样是为了调用的Channel不被析构吧?和线程安全有关。
如果tied_标志为flase,直接调用handleEventWithGuard。
void Channel::handleEventWithGuard(Timestamp receiveTime)
首先把eventHandling_置为真,然后
LOG_TRACE << reventsToString()
然后会根据revents_的状况调用不同的回调函数或者作不同的处理,最后将eventHandling_。
Poller
typedef std::map<int, Channel*> ChannelMap;
ChannelMap channels_的对象,一个EventLoop* ownerLoop_指向自己的loop。Poller的实现支持epoll和poll两种模式。使用类的继承实现。多态的方式。声明了很多接口提供loop使用,具体实现的时候借用多态。
Poller(EventLoop* loop)
就使用loop初始化自己的指针。
static Poller* newDefaultPoller(EventLoop* loop)
接受一个loop作为初始化参数,根据使用那种poll分别new出继承对象。比如使用Poll的话,就会new出pollpoller(loop)对象,返回赋给Poller*。
Poller::hasChannel(Channel* channel)
ChannelMap::const_iterator it = channels_.find(channel->fd());
return it != channels_.end() && it->second == channel;
判断poller里面的注册事件是否有参数channel中指定的事件,使用channel中的文件描述符作为参数寻找。
EPollPoller
typedef std::vector EventList;
派生类,派生于Poller。包含了一个epollfd_和Eventlist数据类型。
epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。文件描述符由epoll_create创建。使用初始化事件列表大小值初始化Eventlist,名为events。
EPollPoller::EPollPoller(EventLoop* loop)
用Loop初始化Poller基类部分,然后调用epoll_create返回标识epoll事件表的文件描述符初始化epollfd_。
void EPollPoller::update(int operation, Channel* channel)
每个事件有一个指针,会指向这个事件所属的管道!!
events的data是由我们自己填进去的,你填一个fd也行
我们直接把这个fd所属的管道指针填进去了。
struct epoll_event event; //准备一个event变量
memZero(&event, sizeof event);
event.events = channel->events(); //把这个管道的注册事件类型写入这个event变量中。
event.data.ptr = channel;//把evetns.data的指针指向channel
int fd = channel->fd(); //把这个管道的fd取出来。
然后调用
epoll_ctl(epollfd_, operation, fd, &event)
将这个事件注册进去。这个event是我们注册进去的事件,后续如果这个事件被触发了,会原封不动得输出出来。在epoll_wait的epoll_event*指针中,这个列表就指向了这些被触发的事件。
void EPollPoller::updateChannel(Channel* channel)
参数为要插入epoll事件符中的channel。
首先取出channel的索引,取名为局部变量index。写入LOG信息,表示文件表数字为fd的events事件。
如果索引是-1,那么首先确保channels找不到这个文件描述符的channel,然后把这个channel写入channels_中。以fd为map的键值。
channels_[fd] = channel
如果索引是2,判断是否能找到这个管道中fd对应的管道,然后判断channels_ map结构中的fd键值对应的管道等不等于这个管道。
然后将这个管道得index设置为1(kAdded)。
调用update函数,操作数是把这个管道加入到epoll事件表中。
如果索引不是-1,也不是2,那么就对管道进行更新。
首先要确保这个管道是在poller的注册列表中的,如果这个channel是isNoneEvent,就把这个管道删除。否则调用update(EPOLL_CTL_MOD, channel);
Timestamp poll(int timeoutMs, ChannelList* activeChannels) override
是重写的虚函数。接受一个超时参数,和一个ChannelList参数。是包含Channel的Vector。
调用epollwait
int numEvents = ::epoll_wait(epollfd_,
&*events_.begin(),
static_cast<int>(events_.size()),
timeoutMs);
epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
events Vector的头指针被解引用然后取址赋予给epoll_event*。
如果发生的事件数量大于0,那么就会把发生事件信息写入LOG中。
然后调用fillActiveChannels(numEvents, activeChannels)
功能是把这些事件对应的管道都给加入到activeChannels中。
void EPollPoller::fillActiveChannels(int numEvents,ChannelList* activeChannels) const
首先确保发生的事件数量少于等于事件列表的长度。
遍历发生的事件,首先创建局部变量channel
Channel* channel = static_cast<Channel*>(events_[i].data.ptr)
把这个事件对应的管道给找出来
然后把这个事件的类型设置到这个管道的就绪事件中
最后把这个管道push到activeChannels中。
Eventloopthread
主线程的类,包含了一个Eventloop *loop_ 数据成员,布尔值exiting,Thread类型的值thread_(表示这个loop在哪个线程运行)。一个锁和一个条件变量锁,和一个线程初始回调函数callback_。其需要以Evenloop*为参数。
typedef std::function<void(EventLoop*)> ThreadInitCallback;
EventLoopThread::threadFunc()
是用于初始化Thread类的函数,其首先定义了一个局部变量
Eventloop loop, 在这里子线程创建了eventloop。
如果有创建线程的时候就要调用的函数的话,就会调用这个函数callback_。后面首先会锁住,把这个局部变量的loop地址给loop_(共享的),然后用条件变量通知。之后调用
loop.loop()。
EventLoop* EventLoopThread::startLoop()
会调用thread_.start(),子线程就会运行threadfunc。创建loop并开始loop。
创建一个空的局部变量 Eventloop* loop,并且开始等待loop_不为空。因为当其不为空的时候,就会被通知。然后把loop_的值给局部变量loop,并且返回。
主线程和子线程的共同数据成员是loop_。子线程会把自己的loop给loop_,主线程需要返回这个Loop_。
EventLoopThreadPool
前置声明有EventLoop和EnvetLoopThread
EventLoop* baseLoop_; //??
string name_; //
bool started_;
int numThreads_; //线程池里面有多少个线程
int next_; //下一个被 选用的线程池的索引
std::vector<std::unique_ptr<EventLoopThread>> threads_; //指向每个EventLoopThread的指针向量
std::vector<EventLoop*> loops_;//包含各个子线程loop的指针
EventLoop* EventLoopThreadPool::getNextLoop()
首先确认调用者是baseloop的创建者,把baseloop指针给loop。
然后把loops_的vector中取出下一个eventloop,使用next_作为索引。
把这个loop指针返回。
void EventLoopThreadPool::start(const ThreadInitCallback& cb)
根据numThreads的设定值,初始化出numThreads条线程。
EventLoopThread* t = new EventLoopThread(cb, buf);
是在堆上new出EventloopThread对象。