此学习笔记主要记录对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);
}
}