Muduo之Channel源码解析

Muduo之Channel源码解析

前面我们分析了Acceptor处理连接请求,之后创建TcpConnection对象分发给合适的EventLoop,而TcpConnection里面是包含Channel,EventLoop以及相应的socket fd的,我们就可以假想成TcpConnection是针对一个连接的所有东西(socket,回调函数等)。但是对于socket的事件监听最后是需要装载到Pool里面的,而这里就是通过Channel和EventLoop将它们连接起来的,这篇文章我们就分析Channel类。

我们再次说明Channel类是对于某个socket以及对应的读写事件的封装,并且他也有包含了处理该TcpConnection的线程的EventLoop对象指针,通过Channel可以将监听的事件装载到它的EventLoop里面,因为poll是封装在EventLoop里面,因此最后事件监听还是由系统poll执行(这里poller是一次基类,muduo实现了poll和epoll,分别对应于PollPoller和EPollPoller)。

在Acceptor的章节有分析过Channel的四个接口函数分别提供打开关闭读写事件的功能,我们这边主要分析下它的实现

 53 void Channel::update()
 54 {
 55     /* enableReading can call here
 56      * pass channel object point to EventLoop
 57      */
 58   addedToLoop_ = true;
 59   loop_->updateChannel(this);
 60 }

可以看到这里调用了channel里面的EvetLoop成员的 loop_->updateChannel(),通过这个函数把相应的监听事件装载到EventLoop里面(其实最后是到poller的,不过EventLoop对poller进行了封装,目前我们统一看成是装在到EventLoop里面),我们来看下它的源码:

251 void EventLoop::updateChannel(Channel* channel)
252 {
253   assert(channel->ownerLoop() == this);
254   assertInLoopThread();
255   poller_->updateChannel(channel);
256 }

首先是再次判断下当前Channel和EventLoop是否属于同一个线程,之后调用了poller->updateChannel(),之前我们说过这里运用了C++的多态,poller实际上是个基类,子类有EPollPoller和PollPoller两种,我们讨论EPoll的,看如下源码:

108 void EPollPoller::updateChannel(Channel* channel)
109 {
110   Poller::assertInLoopThread();
111   const int index = channel->index();
112   LOG_TRACE << "fd = " << channel->fd()
113     << " events = " << channel->events() << " index = " << index;
114   if (index == kNew || index == kDeleted)
115   {
116     // a new one, add with EPOLL_CTL_ADD
117     int fd = channel->fd();
118     if (index == kNew)
119     {
120       assert(channels_.find(fd) == channels_.end());
121       channels_[fd] = channel;
122     }
123     else // index == kDeleted
124     {
125       assert(channels_.find(fd) != channels_.end());
126       assert(channels_[fd] == channel);
127     }
128
129     channel->set_index(kAdded);
130     update(EPOLL_CTL_ADD, channel);
131   }
132   else
133   {
134     // update existing one with EPOLL_CTL_MOD/DEL
135     int fd = channel->fd();
136     (void)fd;
137     assert(channels_.find(fd) != channels_.end());
138     assert(channels_[fd] == channel);
139     assert(index == kAdded);
140     if (channel->isNoneEvent())
141     {
142       update(EPOLL_CTL_DEL, channel);
143       channel->set_index(kDeleted);
144     }
145     else
146     {
147       update(EPOLL_CTL_MOD, channel);
148     }
149   }
150 }

从上面的代码可以看到这里用到channel的index成员,这个主要就是指明当前channel的一个监听状态,可以有如下三种类型:

 33 namespace
 34 {
 35 const int kNew = -1;
 36 const int kAdded = 1;
 37 const int kDeleted = 2;
 38 }

如果是kDeleted或者kNew的那么就需要更新channels_列表并且调用EPOLL_CTL_ADD,而kAdded的话那么则是调用EPOLL_CTL_MOD。channels_保存了该poller已有的所有的channel以及对应的状态:

173 void EPollPoller::update(int operation, Channel* channel)
174 {
175   struct epoll_event event;
176   bzero(&event, sizeof event);
177   event.events = channel->events();
178   event.data.ptr = channel;
179   int fd = channel->fd();
180   LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
181     << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
182   if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
183   {
184     if (operation == EPOLL_CTL_DEL)
185     {
186       LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
187     }
188     else
189     {
190       LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
191     }
192   }
193 }

这里最后还是调用到了系统调用的epoll_ctl(),第一个参数是epoll_create1()创建的epollfd,是该poller持有的,而第三个fd才是真正的该channel对应的fd呢。

到此我们就理清了当服务器接收到一个连接请求创建TcpConnection对象并分发给一个EventLoop,之后是如何针对该socket的监听事件装载到所属的EventLoop的poller里面的。注意,一个eventLoop拥有一个poller,对应一个channels列表以及一个epollfd,而它是所属于一个线程的,对于从Acceptor分配给该线程的所有TcpConnection都会加入到channelMap里面,键值就是fd。

现在监听事件已经装载完毕,那么就需要EventLoop跑起来(准确的说是poller跑起来),那么之后如果有事件发生又是如何处理的呢,下一节就分析EventLoop对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值