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對象決定。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM