啥都不会啊!怎么办啊!

Fitz

慢慢来生活总会好起来的!!!

muduo源码分析Channel

 

简介

Channel类,即通道类。Channel类是可能产生事件的文件描述符封装在其中的,这里的文件描述符可以是file descriptor,可以是socket,还可以是timefd,signalfd。但实际上它不拥有fd_,不用负责将其关闭,关闭是Eventpool的事情。

Acceptor和TcpConnection通过Channel与Eventpool建立连接,而且真正的事件处理函数也都是封装在Channel类中的。

每个Channel对象自始至终只属于一个EventLoop,因此每个Channel对象都只属于某一个IO线程。

每个Channel对象自始至终只负责一个文件描述符(fd)的IO事件分发,但它并不拥有这个fd,也不会在析构的时候关闭这个fd。

Muduo用户一般不直接使用Channel,而会使用更上层的封装,如TcpConnection。

Channel的生命期由其owner calss负责管理。

Channel的成员函数都只能在IO线程调用,因此更新数据成员都不必加锁。

 

源码分析

Channel.h源码

 

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is an internal header file, you should not include this.

#ifndef MUDUO_NET_CHANNEL_H
#define MUDUO_NET_CHANNEL_H

#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

#include <muduo/base/Timestamp.h>
/*个人理解:channel是一个具体来处理事件的类,它与eventloop关系紧密,主要是根据事件宏定义来调用对应的回调函数
 *主要的事件有三种,读事件,写事件和结束事件
 **/
namespace muduo {
    namespace net {

        class EventLoop;

///
/// A selectable I/O channel.
///
/// This class doesn't own the file descriptor.
/// The file descriptor could be a socket,
/// an eventfd, a timerfd, or a signalfd
        class Channel : boost::noncopyable {
        public:
            typedef boost::function<void()> EventCallback;
            typedef boost::function<void(Timestamp)> ReadEventCallback;//读事件的回调函数中必须有参数Timestamp

            Channel(EventLoop *loop, int fd);//一个channel要绑定一个EventLoop和一个文件描述符,但channel无权操作fd
            ~Channel();

            void handleEvent(Timestamp receiveTime);//处理事件
            void setReadCallback(const ReadEventCallback &cb) { readCallback_ = cb; }

            void setWriteCallback(const EventCallback &cb) { writeCallback_ = cb; }

            void setCloseCallback(const EventCallback &cb) { closeCallback_ = cb; }

            void setErrorCallback(const EventCallback &cb) { errorCallback_ = cb; }//设置四种回调函数

            /// Tie this channel to the owner object managed by shared_ptr,
            /// prevent the owner object being destroyed in handleEvent.
            //这个函数,用于延长某些对象的生命期,使其寿命长过Channel::handleEvent()函数。
            void tie(const boost::shared_ptr<void> &);//将一个shared_ptr指针的值赋给tie_

            int fd() const { return fd_; }

            int events() const { return events_; }

            void set_revents(int revt) { revents_ = revt; } // used by pollers
            // int revents() const { return revents_; }
            bool isNoneEvent() const { return events_ == kNoneEvent; }//判断事件是否为0,也就是没有关注的事件

            void enableReading() {
                events_ |= kReadEvent;
                update();
            }//设置读事件,并将当前channel加入到poll队列当中
            // void disableReading() { events_ &= ~kReadEvent; update(); }
            void enableWriting() {
                events_ |= kWriteEvent;
                update();
            }//设置写事件,并将当前channel加入到poll队列当中
            void disableWriting() {
                events_ &= ~kWriteEvent;
                update();
            }//关闭写事件,并将当前channel加入到poll队列当中
            void disableAll() {
                events_ = kNoneEvent;
                update();
            }//关闭所有事件,并暂时删除当前channel
            bool isWriting() const { return events_ & kWriteEvent; }//是否关注写事件

            // for Poller
            int index() { return index_; }//返回序号
            void set_index(int idx) { index_ = idx; }//设置序号

            // for debug
            string reventsToString() const;

            void doNotLogHup() { logHup_ = false; }//把挂起标志位置false

            EventLoop *ownerLoop() { return loop_; }

            void remove();

        private:
            void update();

            void handleEventWithGuard(Timestamp receiveTime);

            static const int kNoneEvent;
            static const int kReadEvent;
            static const int kWriteEvent;

            EventLoop *loop_;            // 所属EventLoop
            const int fd_;            // 文件描述符,但不负责关闭该文件描述符
            int events_;        // 需要epoll关注的事件
            int revents_;        // poll/epoll wait返回的需要处理的事件
            int index_;        // used by Poller.表示在epoll队列中的状态:1.正在队列中2.曾经在队列中3.从来没在队列中
            bool logHup_;        // for POLLHUP是否被挂起

            boost::weak_ptr<void> tie_;//保证channel所在的类
            bool tied_;
            bool eventHandling_;        // 是否处于处理事件中
            ReadEventCallback readCallback_;//当文件描述符产生读事件时,最后调用的读函数,我将它命名为channel的读函数
            EventCallback writeCallback_;//当文件描述符产生写事件时,最后调用的写函数,我将它命名为channel的写函数
            EventCallback closeCallback_;//当文件描述符产生关闭事件时,最后调用的关闭函数,我将它命名为channel的关闭函数
            EventCallback errorCallback_;//当文件描述符产生错误事件时,最后调用的错误函数,我将它命名为channel的错误函数
        };

    }
}
#endif  // MUDUO_NET_CHANNEL_H

 

Channel.cc源码

 

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>

#include <sstream>

#include <poll.h>

using namespace muduo;
using namespace muduo::net;

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;

Channel::Channel(EventLoop *loop, int fd__)
        : loop_(loop),
          fd_(fd__),
          events_(0),
          revents_(0),
          index_(-1),//就是kNew
          logHup_(true),
          tied_(false),
          eventHandling_(false) {
}

Channel::~Channel() {
    assert(!eventHandling_);
}

void Channel::tie(const boost::shared_ptr<void> &obj)//给tie_指针赋值,tie_指针是一个weak_ptr指针,但是给weak_ptr指针赋值的一定是一个shared_ptr指针
{
    tie_ = obj;
    tied_ = true;
}

void Channel::update()//把当前的channel加入到poll队列当中
{
    loop_->updateChannel(this);
}

// 调用这个函数之前确保调用disableAll
// 从EventLoop中移除这个channel
void Channel::remove() {
    assert(isNoneEvent());
    loop_->removeChannel(this);
}

void Channel::handleEvent(Timestamp receiveTime)//Timestamp主要用于读事件的回调函数
{
    boost::shared_ptr<void> guard;
    if (tied_) {
        guard = tie_.lock();//提升tie_为shared_ptr,如果提升成功,说明指向一个存在的对象
        if (guard) {
            LOG_TRACE << "[6] usecount=" << guard.use_count();
            handleEventWithGuard(receiveTime);
            LOG_TRACE << "[12] usecount=" << guard.use_count();
        }
    } else {
        handleEventWithGuard(receiveTime);
    }
}

void Channel::handleEventWithGuard(Timestamp receiveTime)
//暂时理解:查看epoll/或者poll返回的具体是什么事件,并根据事件的类型进行相应的处理
{
    eventHandling_ = true;
    /*
    if (revents_ & POLLHUP)
    {
        LOG_TRACE << "1111111111111111";
    }
    if (revents_ & POLLIN)
    {
        LOG_TRACE << "2222222222222222";
    }
    */
    if ((revents_ & POLLHUP) && !(revents_ & POLLIN))//当事件为挂起并没有可读事件时
    {
        if (logHup_) {
            LOG_WARN << "Channel::handle_event() POLLHUP";
        }
        if (closeCallback_) closeCallback_();
    }

    if (revents_ & POLLNVAL)//描述字不是一个打开的文件描述符
    {
        LOG_WARN << "Channel::handle_event() POLLNVAL";
    }

    if (revents_ & (POLLERR | POLLNVAL))//发生错误或者描述符不可打开
    {
        if (errorCallback_) errorCallback_();
    }
    if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))//关于读的事件
    {
        if (readCallback_) readCallback_(receiveTime);
    }
    if (revents_ & POLLOUT)//关于写的事件
    {
        if (writeCallback_) writeCallback_();
    }
    eventHandling_ = false;
}

string Channel::reventsToString() const//把事件编写成一个string
{
    std::ostringstream oss;
    oss << fd_ << ": ";
    if (revents_ & POLLIN)
        oss << "IN ";
    if (revents_ & POLLPRI)
        oss << "PRI ";
    if (revents_ & POLLOUT)
        oss << "OUT ";
    if (revents_ & POLLHUP)
        oss << "HUP ";
    if (revents_ & POLLRDHUP)
        oss << "RDHUP ";
    if (revents_ & POLLERR)
        oss << "ERR ";
    if (revents_ & POLLNVAL)
        oss << "NVAL ";

    return oss.str().c_str();
}

 

Channel::tie()详解

这里是一个智能指针使用的特定场景之一,用于延长特定对象的生命期

结合例子分析,看下面的一个调用时序图

 

 

 

 

当对方断开TCP连接,这个IO事件会触发Channel::handleEvent()调用,后者会调用用户提供的CloseCallback,而用户代码在onClose()中有可能析构Channel对象,这就造成了灾难。等于说Channel::handleEvent()执行到一半的时候,其所属的Channel对象本身被销毁了。这时程序立刻core dump就是最好的结果了。

Muduo的解决办法是提供Channel::tie(const boost::shared_ptr<void>&)这个函数,用于延长某些对象(可以是Channel对象,也可以是其owner对象)的生命期,使之长过Channel::handleEvent()函数。

Muduo TcpConnection采用shared_ptr管理对象生命期的原因之一就是因为这个。

当有关闭事件时,调用流程如下:

Channel::handleEvent -> TcpConnection::handleClose ->TcpClient::removeConnection ->TcpConnection::connectDestroyed->channel_->remove()。

1、为了在Channel::handleEvent处理期间,防止因其owner对象被修改,进而导致Channel被析构,最后出现不可预估错误。 Channel::tie()的作用就是将Channel的owner对象进行绑定保护起来。

 2、另外channel->remove的作用是删除channel在Poll中的地址拷贝,而不是销毁channel。channel的销毁由其owner对象决定。

 

posted @ 2020-04-14 18:42  Fitz~  阅读(1161)  评论(0编辑  收藏  举报