精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Epoll封裝類實現

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-13 11:54 ? 次閱讀

關于epoll的原理,以及和poll、select、IOCP之間的比較,網上的資料很多,這些都屬于I/O復用的實現方法,即可以同時監聽發生在多個I/O端口(socket套接字描述符或文件描述符)的事件,并將事件從內核通知到用戶區,實現對特定事件的響應處理,而epoll可認為是poll的改進版,在多個方面大幅度提高了性能(當然也是在監聽描述符多、活躍描述符少的條件下)。

epoll的主要特點有以下幾點:

  • 1.支持一個進程打開最大數目的socket描述符,通常數目只受限于系統內存;
  • 2.IO效率不隨FD數目的增加而下降,它只對“活躍”的socket進行操作;
  • 3.使用內存映射加速內核與用戶空間的消息傳遞。

這里只是簡單介紹了epoll的幾個重要特征,總之,epoll的高性能使其在服務器網絡連接層開發中應用的很廣泛,包括很多開源的服務器框架底層也采用了epoll。下面我們主要來設計實現一個epoll操作封裝類。

首先說明一下,epoll主要三個操作函數:

  1. int epoll_create(int size);

創建一個epoll的句柄。自從linux2.6.8之后,size參數是被忽略的。需要注意的是,當創建好epoll句柄后,它就是會占用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所以在使用完epoll后,必須調用close()關閉,否則可能導致fd被耗盡。

  1. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll的事件注冊函數,它不同于select()是在監聽事件時告訴內核要監聽什么類型的事件,而是在這里先注冊要監聽的事件類型。

第一個參數是epoll_create()的返回值。

第二個參數表示動作,用三個宏來表示:

EPOLL_CTL_ADD:注冊新的fd到epfd中;

EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;

EPOLL_CTL_DEL:從epfd中刪除一個fd;

第三個參數是需要監聽的fd。

第四個參數是告訴內核需要監聽什么事件,struct epoll_event結構如下:

typedef union epoll_data
{
pointer ptr;
int fd;
uint u32;
uint64 u64;
} epoll_data_t;

struct epoll_event
{
uint events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

events可以是以下幾個宏的集合:

  • EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
  • EPOLLOUT:表示對應的文件描述符可以寫;
  • EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來);
  • EPOLLERR:表示對應的文件描述符發生錯誤;
  • EPOLLHUP:表示對應的文件描述符被掛斷;
  • EPOLLET:將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對于水平觸發(Level Triggered)來說的。
  • EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里。

epoll_data_t是一個聯合結構,64位大小,可以存fd。這里具體實現中我們存一個CEpollObject對象的指針,以確保epoll_wait從網絡中接收到的消息確實是我們通過一個CEpollObject對象監聽到的。

  1. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

收集在epoll監控的事件中已經發送的事件。參數events是分配好的epoll_event結構體數組,epoll將會把發生的事件賦值到events數組中(events不可以是空指針,內核只負責把數據復制到這個events數組中,不會去幫助我們在用戶態中分配內存)。maxevents告之內核這個events有多大,這個 maxevents的值不能大于創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。如果函數調用成功,返回對應I/O上已準備好的文件描述符數目,如返回0表示已超時。

Epoll封裝類實現

設計思想:通過一個模板類實現向Epoll注冊、修改和刪除事件等操作,需要使用epoll類都必須走這個模塊類,類似一種委托的功能回調模板類實例化對象的epoll監聽事件響應處理操作,主要實現類:CEpollObjectInf、CEpoll和CEpollObject模板類。

CEpollObjectInf類的實現

主要功能:表達epoll_data_t的存儲內容,以及提供對epoll_wait監聽到的事件提供響應處理接口

實現代碼:

#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
#include < sys/epoll.h >
#define INVAILD_SOKET (~0)
class CEpoll;
class CEpollObjectInf
{
    friend class CEpoll;
    protected:
        //這兩個變量為CEpoll的WaitAndEvent中做對象合法檢驗,
        //如果是64位系統,則不要SOCKET變量
        CEpoll *m_pstEpoll;
        SOCKET m_iSocket;
    public:
        CEpollObjectInf()
            :m_pstEpoll(NULL),
            m_iSocket(INVAILD_SOKET )
            {}
        virtual CEpollObjectInf(){}
    protected:
        virtual void OnEpollEvent(int iEvent) = 0;
}

CEpoll類的實現

主要功能:封裝epoll的各項操作

代碼實現:

#define UINT64_MAKE(high, low) ((uint64)(((unsigned int)((low) & 0xFFFFFFFF)) | ((uint64)((unsigned int)((high) & 0xFFFFFFFF))) < < 32))
#define UINT64_LOW(i) ((unsigned int)((uint64)(i) & 0xFFFFFFFF))
#define UINT64_HIGH(i) ((unsigned int)((uint64)(i) > > 32))
class CEpoll
{
    public:
        CEpoll()
            :m_kdpfd(0),
             m_size(0),
             m_iWaitSize(0),
             m_astEvents(0)
        {}
        virtual ~CEpoll()
        {
            Exit();
        }
    public:
        //初始化
        int Init(int iEpollSize, int iWaitSize)
        {
            m_size = iEpollSize;
            m_iWaitSize = iWaitSize;
            m_astEvents = new epoll_event[m_iWaitSize];
            if(!m_astEvents)
                return -1;
            m_kdpfd = epoll_create(m_size);
            if(m_kdpfd < 0)
                return -2;
            return 0;
        }
        /**
        *等待時間發生或超時
        *iTimeout 等待的超時時限單位毫秒
        *return < 0 表示出錯 =0表示超過時間 >0 表示收到并處理的事件個數
        **/
        int Wait(int iTimeOut)
        {
            return epoll_wait(m_kdpfd,m_astEvents,m_iWaitSize,iTimeOut);
        }
        /**
        *等待事件發生或超時,并調用方法
        *iTimeOut 等待超時時限,單位毫秒
        *return < 0 表示出錯 =0 表示沒有 >0 表示收到并處理的事件個數
        */
        int WaitAndEvent(int iTimeOut)
        {
            int iEventCount = Wait(iTimeOut);
            if(iEventCount < 0)
            {
                return iEventCount;
            }
            else if(iEventCount == 0) // 超時
            {
                return 0;
            }

            //一次最多處理1000個事件
            for(int i = 0;i < iEventCount && i < 1000; ++i)
            {
                //在64位系統下uData只能存放一個指針
                uint64 uData = GetData(i);
            #ifdef BIT64
                CEpollObjectInf *pstObjectPos = (CEpollObjectInf *)uData;
            #else
                CEpollObjectInf *pstObjectPos = (CEpollObjectInf *)(UINT64_LOW(uData));
            #endif

                uint uiEvent = GetEvent(i); //  event
                //判斷對象是否合法
                if(pstObjectPos == NULL || pstObjectPos- >m_pstEpoll != this)
                {
                    //不處理本次事件,繼續處理下一個事件
                    continue;
                }
                pstObjectPos- >OnEpollEvent(uiEvent);
            }
            return iEventCount;
        }

        uint64 GetData(int i) const
        {
            ASSERT(i < m_iWaitSize)
            return m_astEvents[i].data.u64;
        }

        uint GetEvent(int i) const
        {
            ASSERT(i < m_iWaitSize)
            return m_astEvents[i].events;
        }

        static bool IsInputEvent(int iEvent)
        {
            return (iEvent & EPOLLIN) != 0;
        }
        static bool IsOutputEvent(int iEvent) 
        { 
            return (iEvent & EPOLLOUT) != 0; 
        }
        static bool IsCloseEvent(int iEvent) 
        { 
            return (iEvent & (EPOLLHUP|EPOLLERR)) != 0; 
        }

        int Add(SOCKET s, uint64 data, uint event)
        {
            m_stEvent.events = event|EPOLLERR|EPOLLHUP;
            m_stEvent.data.u64 = data;
            int iRet = epoll_ctl(m_kdpfd,EPOLL_CTL_ADD,s,&m_stEvent);
            return iRet;
        }
        int Del(SOCKET s, uint64 data = 0, uint event = EPOLLIN)
        {
            m_stEvent.events = 0;
            m_stEvent.data.u64 = data;
            int iRet = epoll_ctl(m_kdpfd,EPOLL_CTL_DEL,s,&m_stEvent);
            return iRet;
        }
        int Mod(SOCKET s, uint64 data, uint event)
        {
            m_stEvent.events   = event|EPOLLERR|EPOLLHUP;
            m_stEvent.data.u64 = data;
            int iRet = epoll_ctl(m_kdpfd,EPOLL_CTL_MOD,s,&m_stEvent);
            return iRet;
        }

     protected:
        void Exit()
        {
            if(m_astEvents)
            {
               delete []m_astEvents;
               m_astEvents = 0;
            }
            if(m_kdpfd > 0)
            {
               close(m_kdpfd);
               mkdpfd = 0;
            }
       }
    protected:
        int                m_kdpfd;
        int                m_size;
        int                 m_iWaitSize;
        struct epoll_event *m_astEvents;
        struct epoll_event m_stEvent;

};

CEpollObject模版類的實現

主要功能:托管CEpoll類的具體操作,注冊事件到Epoll,必須實例化CEpollObject模版類,并覆蓋實現具體的事件處理函數,以回調不同對象對事件的處理函數。

代碼實現:

template< typename Owner >        
class CEpollObject: public CEpollObjectInf
{
    friend class CEpoll;
    public:
        typedef void (Owner::*PF_EPOLL_EVENT)(CEpollObject *pstObject,Socket iSocket, int iEvnet);
    protected:
        Owner                  *m_pstOwner;
        PF_EPOLL_EVENT         m_pfEvent;
        unsigned int        m_iRegEvent;
    public:
        CEpollObject()
            :m_pstOwner(NULL),
             m_pfEvent(NULL),
             m_iRegEvent(0)
        {}
        virtual ~CEpollObject() {Unregister();}

        /**
        *注冊到Epoll中
        **/
        int Register(Owner &stOwner, PF_EPOLL_EVENT pfEvent,CEpoll &stEpoll,SOCKET iSocket, unsigned int iRegEvent)
{
            ASSERT(iSocket != INVALID_SOCKET && iRegEvent > 0 && pfEvent != NULL);
            int iRet = Unregister();
            if(iRet)
                return iRet;
            m_pstOwner = &stOwner;
            m_pstEpoll = &stEpoll;
            m_pfEvent  = pfEvent;
            m_iRegEvent = iRegEvent;
            m_iSocket   = iSocket;

            uint64 uData = CreateData(m_iSocket);
            iRet = m_pstEpoll- >Add(m_iSocket,uData,m_iRegEvent);
            return iRet;
        }

        /**
        *更改關注的事件
        **/
        int ModRegEvent(int iRegEvent)
{
            m_iRegEvent = iRegEvent;
            if(m_pstEpoll)
            {
                uint64 uData = CreateData(m_iSocket);
                return m_pstEpoll- >Mod(m_iSocket,uData,m_iRegEvent);
            }
            return 0;
        }

    protected:
        virtual void OnEpollEvent(int iEvent)
{
            ASSERT(m_pstOwner != NULL && m_pfEvent != NULL);
            (m_pstOwner- >*m_pfEvent)(this,m_iSocket,iEvent);
        }

        int Unregister()
{
            int iRet = 0;
            if(m_pstEpoll)
            {
                iRet = m_pstEpoll- >Del(m_iSocket);
                m_pstEpoll = NULL;
            }
            m_pstOwner = NULL;
            m_pfEvent = NULL;
            m_iRegEvent = 0;
            m_iSocket = INVALID_SOCKET;
            return iRet;
        }
        uint64 CreateData(SOCKET iSocket)
{
        #ifdef BIT64
            return (uint64)this;
        #else
            return UINT64_MAKE(iSocket, (unsigned int)this);
        #endif
        }
};
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 封裝
    +關注

    關注

    126

    文章

    7784

    瀏覽量

    142724
  • 服務器
    +關注

    關注

    12

    文章

    9021

    瀏覽量

    85184
  • 端口
    +關注

    關注

    4

    文章

    955

    瀏覽量

    32014
  • epoll
    +關注

    關注

    0

    文章

    28

    瀏覽量

    2947
收藏 人收藏

    評論

    相關推薦

    epoll的使用

    以下內容是參考華清遠見《linux/unix系統編程手冊》對epoll的一個個人總結,是我在華清遠見比較全面的總結。一、epoll的優點同I/O多路復用和信號驅動I/O一樣,linux的epoll
    發表于 05-11 13:22

    我讀過的最好的epoll講解

    認為O(1)的[更新 1]) 在討論epoll實現細節之前,先把epoll的相關操作列出[更新 2]:·epoll_create 創建一個epol
    發表于 05-12 15:30

    epoll使用方法與poll的區別

    因為epoll的觸發機制是在內核中直接完成整個功能 那個事件準備就緒我就直接返回這個IO事件
    發表于 07-31 10:03

    epoll_wait的事件返回的fd為錯誤是怎么回事?

    netlink 的 socket 連接 的 fd 為18,但是添加到epollepoll_wait()返回的fd 為 0為什么會出現這樣的現象?補充 說明:1、 epoll_wait返回
    發表于 06-12 09:03

    揭示EPOLL一些原理性的東西

    我們對這些流的操作都是有意義的。(復雜度降低到了O(1))在討論epoll實現細節之前,先把epoll的相關操作列出:epoll_create 創建一個
    發表于 08-24 16:32

    【米爾王牌產品MYD-Y6ULX-V2開發板試用體驗】socket通信和epoll

    。如果客端連接斷開后,主服務端也就斷開。學習了博客園的@liangf27的帖子來實現單線程服務多個客戶端。修改main.c代碼如下:#include <stdio.h>
    發表于 11-10 15:31

    關于Epoll,你應該知道的那些細節

    Epoll,位于頭文件sys/epoll.h,是Linux系統上的I/O事件通知基礎設施。epoll API為Linux系統專有,于內核2.5.44中首次引入,glibc于2.3.2版本加入支持。其它提供類似的功能的系統,包括F
    發表于 05-12 09:25 ?1193次閱讀

    poll&&epollepoll實現

    poll&&epollepoll實現
    發表于 05-14 14:34 ?2772次閱讀
    poll&&<b class='flag-5'>epoll</b>之<b class='flag-5'>epoll</b><b class='flag-5'>實現</b>

    Linux中epoll IO多路復用機制

    epoll 是Linux內核中的一種可擴展IO事件處理機制,最早在 Linux 2.5.44內核中引入,可被用于代替POSIX select 和 poll 系統調用,并且在具有大量應用程序請求時能夠
    發表于 05-16 16:07 ?699次閱讀
    Linux中<b class='flag-5'>epoll</b> IO多路復用機制

    深度剖析Linux的epoll機制

    在 Linux 系統之中有一個核心武器:epoll 池,在高并發的,高吞吐的 IO 系統中常常見到 epoll 的身影。 IO 多路復用 在 Go 里最核心的是 Goroutine ,也就是所謂的協
    的頭像 發表于 07-29 10:52 ?1393次閱讀

    一文詳解epoll實現原理

    本文以四個方面介紹epoll實現原理,1.epoll的數據結構;2.協議棧如何與epoll通信;3.epoll線程安全如何加鎖;4.ET與
    的頭像 發表于 08-01 13:28 ?4017次閱讀

    epoll實現多路復用

    本人用epoll實現多路復用,epoll觸發模式有兩種: ET(邊緣模式) LT(水平模式) LT模式 是標準模式,意味著每次epoll_wait()返回后,事件處理后,如果之后還有
    的頭像 發表于 11-09 10:15 ?485次閱讀
    用<b class='flag-5'>epoll</b>來<b class='flag-5'>實現</b>多路復用

    epoll實現原理

    今兒我們就從源碼入手,來幫助大家簡單理解一下 epoll實現原理,并在后邊分析一下,大家都說 epoll 性能好,那到底是好在哪里。 epoll 簡介 1、
    的頭像 發表于 11-09 11:14 ?503次閱讀
    <b class='flag-5'>epoll</b> 的<b class='flag-5'>實現</b>原理

    epoll的基礎數據結構

    一、epoll的基礎數據結構 在開始研究源代碼之前,我們先看一下 epoll 中使用的數據結構,分別是 eventpoll、epitem 和 eppoll_entry。 1、eventpoll 我們
    的頭像 發表于 11-10 10:20 ?764次閱讀
    <b class='flag-5'>epoll</b>的基礎數據結構

    epoll源碼分析

    個函數進行源碼分析。 源碼來源 由于epoll實現內嵌在內核中,直接查看內核源碼的話會有一些無關代碼影響閱讀。為此在GitHub上寫的簡化版TCP/IP協議棧,里面實現epoll
    的頭像 發表于 11-13 11:49 ?991次閱讀
    <b class='flag-5'>epoll</b>源碼分析