muduo使用的是reactor模式,关于muduo的其他内容不做过多赘述。此文作为自己阅读muduo源码的笔记,目的在于用直白的语言记录阅读时的理解。
muduo网络库,分为两个部分。base模块主要为实现网络库的一些基础工具,如互斥锁、条件变量、日志库、线程池等等。net模块即是reactor模式的网络通信主要实现部分。
1. net
个人阅读开源代码喜欢从它的使用方法示例看起。先从最顶层了解库的使用方式,再自顶向下逐渐深入,类似于广度优先遍历的方式。然后在感兴趣的部分有选择性的继续深入。
先看一个《Linux多线程服务器端编程》中的例子:
#include <sys/timerfd.h>
muduo::EventLoop* g_loop;
void timeout()
{
printf("Timeout!\n");
g_loop->quit();
}
int main()
{
muduo::EventLoop loop;
g_loop = &loop;
int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
muduo::Channel channel(&loop, timerfd);
channel.setReadCallback(timeout);
channel.enableReading();
struct itimerspec howlong;
bzero(&howlong, sizeof howlong);
howlong.it_value.tv)sec = 5;
::timerfd_settinme(timerfd , 0, &howlong, NULL);
loop.loop();
::close(timefd);
}
上段代码,出现了两个重要的类EventLoop和 Channel,Channel的构造函数使用了EventLoop,并且channel对象可以设置回调函数(setReadCallback()),以及设置关心的回调事件(enableReading())。最后调用loop.loop()开启监听。
1.1 EventLoop & Channel & Poller
EventLoop理解为Poller运转的引擎。
Poller实现了poll或者epoll,用来监听Channel。
Channel对应描述符,绑定了描述符事件的处理方法。
先看一下EventLoop的数据成员
private:
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_);
可以看到其中有Channel对象的列表activeChannels。这个列表的内容是如何填充的呢?在代码中搜索,这个列表,发现在EventLooop的loop函数定义中对activeChannels进行了修改。
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread();
looping_ = true;
quit_ = false; // FIXME: what if someone calls quit() before loop() ?
LOG_TRACE << "EventLoop " << this << " start looping";
while (!quit_)
{
activeChannels_.clear();
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); //此处通过poller对象填充channels列表。
++iteration_;
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels();
}
// TODO sort channel by priority
eventHandling_ = true;
for (Channel* channel : activeChannels_)
{
currentActiveChannel_ = channel;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = NULL;
eventHandling_ = false;
doPendingFunctors();
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
这里涉及了一个新的poller对象。通过poller对象的poll函数填充了channels列表,并以此调用了列表中channel的handleEvent方法。由此大概可以猜测出,channel由poller管理,poller循环取出活动状态的channle,并触发其handleEvent方法。
接下来分析channel是如何与poller绑定的。
查看poller的接口:
class Poller : noncopyable
{
public:
typedef std::vector<Channel*> ChannelList;
Poller(EventLoop* loop);
virtual ~Poller();
/// Polls the I/O events.
/// Must be called in the loop thread.
virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;
/// Changes the interested I/O events.
/// Must be called in the loop thread.
virtual void updateChannel(Channel* channel) = 0;
/// Remove the channel, when it destructs.
/// Must be called in the loop thread.
virtual void removeChannel(Channel* channel) = 0;
virtual bool hasChannel(Channel* channel) const;
static Poller* newDefaultPoller(EventLoop* loop);
void assertInLoopThread() const
{
ownerLoop_->assertInLoopThread();
}
...
}
可以看出,poller实际上是一个虚基类,在muduo中有有两个poller的具体实现,EPollPoller与PollPoller。跳转到EPollPoller::updateChannel()
void EPollPoller::updateChannel(Channel* channel)
{
Poller::assertInLoopThread();
const int index = channel->index();
LOG_TRACE << "fd = " << channel->fd()
<< " events = " << channel->events() << " index = " << index;
if (index == kNew || index == kDeleted)
{
// a new one, add with EPOLL_CTL_ADD
int fd = channel->fd();
if (index == kNew)
{
assert(channels_.find(fd) == channels_.end());
channels_[fd] = channel;
}
else // index == kDeleted
{
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
}
channel->set_index(kAdded);
update(EPOLL_CTL_ADD, channel);
}
else
{
// update existing one with EPOLL_CTL_MOD/DEL
int fd = channel->fd();
(void)fd;
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
assert(index == kAdded);
if (channel->isNoneEvent())
{
update(EPOLL_CTL_DEL, channel);
channel->set_index(kDeleted);
}
else
{
update(EPOLL_CTL_MOD, channel);
}
}
}
可以看出,poller对象维护了一个channels[]数组,通过updateChannel向其中添加channel。通过查找该方法的引用,跳转到EventLoop::updateChannel();
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
继续查找 EventLoop::updateChannel(Channel* channel)的引用。跳转到void Channel::update();
void Channel::update()
{
addedToLoop_ = true;
loop_->updateChannel(this);
}
再查找Channel::update()的引用,跳转到void enableReading()
void enableReading() { events_ |= kReadEvent; update(); }
通过以上分析,基本上清楚了三个类之间的关系以及作用。
Poller中绑定了关心的Channle,并监听Channel的活动。
EventLoop发起循环,使得Poller循环输出active的Channel,并调用Channel的回调函数。