日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

muduo网络库学习(一)对io复用的封装Poller,面向对象与基于对象

發(fā)布時間:2024/4/19 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 muduo网络库学习(一)对io复用的封装Poller,面向对象与基于对象 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

高效并發(fā)的網(wǎng)絡(luò)框架大多離不開io多路復(fù)用函數(shù),Linux下有三種

  • select
  • poll
  • epoll
  • 關(guān)于三者的區(qū)別可以參考 linux網(wǎng)絡(luò)編程—–幾種服務(wù)器模型及io多路復(fù)用函數(shù)
    前段時間看Libevent源碼時也學(xué)習(xí)過對epoll/poll/select的封裝,但是畢竟c語言寫的庫,是通過函數(shù)指針實現(xiàn)多態(tài)。我學(xué)習(xí)的muduo源碼是c++11版本的,利用c++進行封裝。


    首先復(fù)習(xí)一下C++面向?qū)ο蠛突趯ο蟮膮^(qū)別

    • 面向?qū)ο?
      • 面向?qū)ο蟮娜筇攸c:封裝,繼承,多態(tài)缺一不可
      • 封裝:數(shù)據(jù)和處理數(shù)據(jù)的函數(shù)統(tǒng)一起來,封裝在一個class中
      • 繼承:通過繼承某個類派生出一個新類,被繼承的類稱作基類,派生出的類稱作派生類。派生類是對基類的補充,二者之間滿足一定的歸屬關(guān)心,如動物(基類),鳥(派生類)。繼承可以是public/private/protected繼承,也可以是虛繼承(用于解決多重繼承帶來的重復(fù)問題),基類可以是抽象基類(不能被實例化),但是派生類需要重新實現(xiàn)基類定義的每個純虛函數(shù)。
      • 多態(tài):在繼承的基礎(chǔ)上通過基類指針指向派生類的實例化對象,達(dá)到調(diào)用派生類虛函數(shù)的目的,多態(tài)又被叫做運行時多態(tài),是在運行期根據(jù)基類指針實際指向的對象類型判斷調(diào)用哪個函數(shù)的方式。
    • 基于對象
      • 無繼承,無多態(tài),只有封裝
      • 利用類封裝好的接口實現(xiàn)對數(shù)據(jù)的操作

    如何禁止編譯器自動生成拷貝構(gòu)造函數(shù)/賦值運算符

    • 繼承boost::noncopyable
    • 自定義空基類,基類中將兩個函數(shù)放在private域,派生類private繼承該基類
    • 在自己的private域中聲明兩個函數(shù),不予實現(xiàn)

    c++11版本采用第2中,boost版本采用第1中,第三種效果不好,因為錯誤是在鏈接期發(fā)現(xiàn),前兩個是在編譯期


    現(xiàn)如今大多C++程序都是基于對象的,面向?qū)ο笾辉谡麄€程序中占一小部分比重。
    muduo采用的也是基于對象的手法,但是對io多路復(fù)用的封裝采用的是面向?qū)ο?#xff0c;即定義一個基類,派生出不同的派生類。muduo只派生了poll/epoll兩個類封裝,因為二者在實現(xiàn)上有相似性,可以共用基類。

    基類Poller主要用于設(shè)計統(tǒng)一接口,兩個派生類EPollPoller/PollPoller用于實現(xiàn)各自的操作,Poller定義如下

    /* 禁止編譯器自動生成拷貝構(gòu)造函數(shù)/賦值操作運算符 */ class Poller : noncopyable {public:typedef std::vector<Channel*> ChannelList;Poller(EventLoop* loop);virtual ~Poller();/// Polls the I/O events./// Must be called in the loop thread./* * 監(jiān)聽函數(shù),對于epoll是epoll_wait,對于poll是poll * 返回epoll_wait/poll返回的時間*/virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;/// Changes the interested I/O events./// Must be called in the loop thread./* 更新監(jiān)聽事件,增刪改對fd的監(jiān)聽事件 */virtual void updateChannel(Channel* channel) = 0;/// Remove the channel, when it destructs./// Must be called in the loop thread./* 刪除監(jiān)聽事件 */virtual void removeChannel(Channel* channel) = 0;virtual bool hasChannel(Channel* channel) const;static Poller* newDefaultPoller(EventLoop* loop);void assertInLoopThread() const{ownerLoop_->assertInLoopThread();}protected:/* * Channel,保存fd和需要監(jiān)聽的events,以及各種回調(diào)函數(shù)(可讀/可寫/錯誤/關(guān)閉等)* 類似libevent的struct event*/typedef std::map<int, Channel*> ChannelMap;/* 保存所有事件Channel,類似libevent中base的注冊隊列 */ChannelMap channels_;private:/* * EventLoop,事件驅(qū)動主循環(huán),用于調(diào)用poll函數(shù)* 類似libevent的struct event_base*/EventLoop* ownerLoop_; };

    類中采用前向聲明,即在定義Poller之前聲明一下class Channel;,用處是避免讓頭文件#include <muduo/net/Channel.h>從而增加依賴性,因為頭文件中并沒有使用Channel,只是定義了這個類型的變量,所以只聲明就好了,而在成員函數(shù)的實現(xiàn)中需要使用Channel的接口,這就需要讓編譯器知道Channel是怎么定義的,就需要在.cpp文件中#include <muduo/net/Channel.h>。另外,因為Channel是Poller的成員變量,當(dāng)Poller析構(gòu)時也會調(diào)用Channel的析構(gòu)函數(shù),這就需要讓編譯器知道Channel析構(gòu)函數(shù)的定義,所以Poller的析構(gòu)函數(shù)需要在.cpp中定義。
    以上也是大多muduo類采用的方法,這種方法可以降低依賴關(guān)系,如果Channel文件改變,不需要重新編譯Poller文件


    派生類EPollPoller的實現(xiàn)就是重新實現(xiàn)基類Poller聲明的純虛函數(shù),簡單的調(diào)用epoll的接口。在poll返回后也會將就緒的fd(muduo是由Channel管理,libevent是由struct event管理)添加到激活隊列中

    /** 對epoll函數(shù)的封裝,繼承自Poller*/ class EPollPoller : public Poller {public:EPollPoller(EventLoop* loop);virtual ~EPollPoller();/* epoll_wait */ virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);/* ADD/MOD/DEL */ virtual void updateChannel(Channel* channel);/* DEL */virtual void removeChannel(Channel* channel);private:static const int kInitEventListSize = 16;/* EPOLL_CTL_ADD/MOD/DEL轉(zhuǎn)成字符串 */static const char* operationToString(int op);/* epoll_wait返回后將就緒的文件描述符添加到參數(shù)的激活隊列中 */void fillActiveChannels(int numEvents,ChannelList* activeChannels) const;/* 由updateChannel/removeChannel間接調(diào)用,執(zhí)行epoll_ctl */void update(int operation, Channel* channel);typedef std::vector<struct epoll_event> EventList;int epollfd_;EventList events_; };

    類的聲明中的EventList記錄著所有監(jiān)聽的epoll_event,.cpp中就是實現(xiàn)上述函數(shù),進行增刪改等,主要記錄一些沒接觸過的知識


    epoll_create1

    int epoll_create(int size);- 創(chuàng)建epollfd,早期linux引入的創(chuàng)建監(jiān)聽epollfd的函數(shù),傳入的參數(shù)size作為給內(nèi)核的一個提示 - 內(nèi)核會根據(jù)這個size分配一塊這么大的數(shù)據(jù)空間用來監(jiān)聽事件(struct epoll_event) - 當(dāng)在使用的過程中出現(xiàn)大于size的值時,內(nèi)核會重新分配內(nèi)存空間。 - 目前這個size已經(jīng)沒有作用,內(nèi)核可以動態(tài)改變數(shù)據(jù)空間大小,但仍然需要傳入大于0的數(shù) int epoll_create1(int flag);- 創(chuàng)建epollfd,新版linux引入的函數(shù),當(dāng)flag為0時和不帶size的epoll_create效果一樣 - 目前flag只支持EPOLL_CLOEXEC,在創(chuàng)建的過程中將返回的epollfd描述符設(shè)置CLOSE-ON-EXEC屬性 - 當(dāng)程序exec執(zhí)行新程序時自動close epollfd

    static_assert

    static_assert(bool flag, char *msg);- 編譯期斷言,程序在編譯的過程中執(zhí)行 - 若flag為真,什么也不做 - 若flag為假,產(chǎn)生一條編譯錯誤,輸出錯誤信息msg,錯誤位置為當(dāng)前行號 static_assert可以增加編譯期對程序的控制,準(zhǔn)確定位出錯的可能

    assert

    assert(bool flag);- 運行期DEBUG模式下的斷言- 若flag為真,什么也不做- 若flag為假,終止程序注意assert只有在debug模式下才會有效,在release模式下這條語句就被編譯器刪除了 對此,通常在assert后面有一個(void)n;等語句int fd = channel_->fd(); assert(channels_[fd] == channel); (void)fd; 如果以release模式下運行,assert被刪除,編譯器會發(fā)出警告通知,提示變量fd未使用 而如果在設(shè)置編譯條件時將警告提升為錯誤,那么編譯就不會繼續(xù)進行 (void)fd;意為將fd轉(zhuǎn)為void類型,簡單使用一下fd,消除警告

    總結(jié)

    以上是生活随笔為你收集整理的muduo网络库学习(一)对io复用的封装Poller,面向对象与基于对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。