此学习笔记主要记录对Muduo源码的理解,主要参考陈硕的《Linux多线程服务端编程》,其他的参考资料会列于笔记末尾。水平有限,若有错误请在我的阅读笔记项目中指出。如果觉得写得还不错,可以点个小星星。
Channel类对象
Channel对象负责一个文件描述符fd的IO事件监听及分发,但fd的生命周期不受Channel管理,因此不会在析构时关闭fd。用户不直接使用Channel,而是利用更上层的封装,例如TcpConnection。Channel涉及的主要时序如下:

EventLoop::loop()函数会调用Poller::poll()函数,返回一个包含当前激活的Channel(发生了监听的events or 错误),然后逐个调用激活Channel的handleEvent()函数(实际会调用handleEventWithGuard完成),进行不同事件的处理。
Channel一般是其他类的直接或间接成员,生命周期由OwnerClass管理。举个栗子,EventLoop的成员wakeupChannel_通过std::unique<Channel>智能指针管理Channel的生命周期,这个Channel是用于唤醒(EventLoop)线程的,具体可见EventLoop分析笔记。
Channel的两大主要职责:
IO事件监听:
监听过程由Poller完成,可见Poller分析笔记。Channel给Poller提供目标文件描述符fd需要监听的事件,例如可读事件POLLIN,可写事件POLLOUT等。其实也就是设置pollfd events相关的内容(这里以poll为例):
1 | // POLL(2) Linux Programmer's Mannual |
当然了,Channel需要提供一些接口给user设置需要监听的事件类型,通过各种enable*和disable*成员函数搞定,具体见后面源码分析。
IO事件分发:
Channel所监听事件rvents发生后,Poller通过Channel::set_revents(int revt)设置Channel::revents_成员变量。后续执行哪些事情(执行回调函数)通过Channel::handleEvent分发,例如fd可写后,会调用writeCallback。writeCallback这些回调函数也通过Channel::set*Callback接口进行设置。
1 | void set_revents(int revt) { revents_ = revt; } |
Channel源码分析
Channel类对象重要成员:

监听事件管理:
enableReading(): 开启读事件监听enableWriting():开启写事件监听disableReading():关闭读事件监听disableWriting():关闭写事件监听disableAll():关闭读写事件监听
以读事件代码为例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
void Channel::enableReading()
{
events_ |= kReadEvent;
update();
}
void Channel::disableReading()
{
events_ &= ~kReadEvent;
update();
}
void Channel::update()
{
addedToLoop_ = true;
loop_->updateChannel(this);
}
回调函数设置:1
2
3
4void setReadCallback(ReadEventCallback cb)
{ readCallback_ = std::move(cb); }
void setWriteCallback(EventCallback cb)
{ writeCallback_ = std::move(cb); }
handleEvent事件处理
事件分发的主要逻辑,即根据revents_的内容调用不同的回调函数。
1 | void Channel::handleEventWithGuard(Timestamp receiveTime) |
tie weak_ptr:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 /*
* tie是weak_ptr: 目的是防止Owener Object在Channel执行handleEvent的时候被析构了
* handleEvent中执行的回调函数其实是TcpConnection中的成员函数,如果析构了就G
* Muduo中Owner Object其实就是TcpConnection
* 当然Owenner Object必须是由shared_ptr管理的
*/
void Channel::tie(const std::shared_ptr<void>& obj)
{
tie_ = obj;
tied_ = true;
}
/*
* 防止的手段就是weak_ptr.lock()尝试提升为shared_ptr
* 提升不成功说明已经被析构了 就不执行handleEventWithGuard
* 提升成功Owner Object不会在handleEventWithGuard中被析构
*/
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}