一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Epoll封裝類實(shí)現(xiàn)

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-13 11:54 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

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

epoll的主要特點(diǎn)有以下幾點(diǎn):

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

這里只是簡單介紹了epoll的幾個(gè)重要特征,總之,epoll的高性能使其在服務(wù)器網(wǎng)絡(luò)連接層開發(fā)中應(yīng)用的很廣泛,包括很多開源的服務(wù)器框架底層也采用了epoll。下面我們主要來設(shè)計(jì)實(shí)現(xiàn)一個(gè)epoll操作封裝類。

首先說明一下,epoll主要三個(gè)操作函數(shù):

  1. int epoll_create(int size);

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

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

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

第一個(gè)參數(shù)是epoll_create()的返回值。

第二個(gè)參數(shù)表示動(dòng)作,用三個(gè)宏來表示:

EPOLL_CTL_ADD:注冊新的fd到epfd中;

EPOLL_CTL_MOD:修改已經(jīng)注冊的fd的監(jiān)聽事件;

EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd;

第三個(gè)參數(shù)是需要監(jiān)聽的fd。

第四個(gè)參數(shù)是告訴內(nèi)核需要監(jiān)聽什么事件,struct epoll_event結(jié)構(gòu)如下:

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可以是以下幾個(gè)宏的集合:

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

epoll_data_t是一個(gè)聯(lián)合結(jié)構(gòu),64位大小,可以存fd。這里具體實(shí)現(xiàn)中我們存一個(gè)CEpollObject對象的指針,以確保epoll_wait從網(wǎng)絡(luò)中接收到的消息確實(shí)是我們通過一個(gè)CEpollObject對象監(jiān)聽到的。

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

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

Epoll封裝類實(shí)現(xiàn)

設(shè)計(jì)思想:通過一個(gè)模板類實(shí)現(xiàn)向Epoll注冊、修改和刪除事件等操作,需要使用epoll類都必須走這個(gè)模塊類,類似一種委托的功能回調(diào)模板類實(shí)例化對象的epoll監(jiān)聽事件響應(yīng)處理操作,主要實(shí)現(xiàn)類:CEpollObjectInf、CEpoll和CEpollObject模板類。

CEpollObjectInf類的實(shí)現(xiàn)

主要功能:表達(dá)epoll_data_t的存儲內(nèi)容,以及提供對epoll_wait監(jiān)聽到的事件提供響應(yīng)處理接口

實(shí)現(xiàn)代碼:

#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:
        //這兩個(gè)變量為CEpoll的WaitAndEvent中做對象合法檢驗(yàn),
        //如果是64位系統(tǒng),則不要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類的實(shí)現(xiàn)

主要功能:封裝epoll的各項(xiàng)操作

代碼實(shí)現(xiàn):

#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;
        }
        /**
        *等待時(shí)間發(fā)生或超時(shí)
        *iTimeout 等待的超時(shí)時(shí)限單位毫秒
        *return < 0 表示出錯(cuò) =0表示超過時(shí)間 >0 表示收到并處理的事件個(gè)數(shù)
        **/
        int Wait(int iTimeOut)
        {
            return epoll_wait(m_kdpfd,m_astEvents,m_iWaitSize,iTimeOut);
        }
        /**
        *等待事件發(fā)生或超時(shí),并調(diào)用方法
        *iTimeOut 等待超時(shí)時(shí)限,單位毫秒
        *return < 0 表示出錯(cuò) =0 表示沒有 >0 表示收到并處理的事件個(gè)數(shù)
        */
        int WaitAndEvent(int iTimeOut)
        {
            int iEventCount = Wait(iTimeOut);
            if(iEventCount < 0)
            {
                return iEventCount;
            }
            else if(iEventCount == 0) // 超時(shí)
            {
                return 0;
            }

            //一次最多處理1000個(gè)事件
            for(int i = 0;i < iEventCount && i < 1000; ++i)
            {
                //在64位系統(tǒng)下uData只能存放一個(gè)指針
                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)
                {
                    //不處理本次事件,繼續(xù)處理下一個(gè)事件
                    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模版類的實(shí)現(xiàn)

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

代碼實(shí)現(xiàn):

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;
        }

        /**
        *更改關(guān)注的事件
        **/
        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
        }
};
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 封裝
    +關(guān)注

    關(guān)注

    128

    文章

    8685

    瀏覽量

    145514
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    13

    文章

    9795

    瀏覽量

    88002
  • 端口
    +關(guān)注

    關(guān)注

    4

    文章

    1046

    瀏覽量

    32954
  • epoll
    +關(guān)注

    關(guān)注

    0

    文章

    28

    瀏覽量

    3164
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    epoll的使用

    以下內(nèi)容是參考華清遠(yuǎn)見《linux/unix系統(tǒng)編程手冊》對epoll的一個(gè)個(gè)人總結(jié),是我在華清遠(yuǎn)見比較全面的總結(jié)。一、epoll的優(yōu)點(diǎn)同I/O多路復(fù)用和信號驅(qū)動(dòng)I/O一樣,linux的epoll
    發(fā)表于 05-11 13:22

    我讀過的最好的epoll講解

    認(rèn)為O(1)的[更新 1]) 在討論epoll實(shí)現(xiàn)細(xì)節(jié)之前,先把epoll的相關(guān)操作列出[更新 2]:·epoll_create 創(chuàng)建一個(gè)epol
    發(fā)表于 05-12 15:30

    epoll使用方法與poll的區(qū)別

    因?yàn)?b class='flag-5'>epoll的觸發(fā)機(jī)制是在內(nèi)核中直接完成整個(gè)功能 那個(gè)事件準(zhǔn)備就緒我就直接返回這個(gè)IO事件
    發(fā)表于 07-31 10:03

    epoll_wait的事件返回的fd為錯(cuò)誤是怎么回事?

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

    揭示EPOLL一些原理性的東西

    我們對這些流的操作都是有意義的。(復(fù)雜度降低到了O(1))在討論epoll實(shí)現(xiàn)細(xì)節(jié)之前,先把epoll的相關(guān)操作列出:epoll_create 創(chuàng)建一個(gè)
    發(fā)表于 08-24 16:32

    【米爾王牌產(chǎn)品MYD-Y6ULX-V2開發(fā)板試用體驗(yàn)】socket通信和epoll

    。如果客端連接斷開后,主服務(wù)端也就斷開。學(xué)習(xí)了博客園的@liangf27的帖子來實(shí)現(xiàn)單線程服務(wù)多個(gè)客戶端。修改main.c代碼如下:#include <stdio.h>
    發(fā)表于 11-10 15:31

    關(guān)于Epoll,你應(yīng)該知道的那些細(xì)節(jié)

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

    poll&&epollepoll實(shí)現(xiàn)

    poll&&epollepoll實(shí)現(xiàn)
    發(fā)表于 05-14 14:34 ?2943次閱讀
    poll&&<b class='flag-5'>epoll</b>之<b class='flag-5'>epoll</b><b class='flag-5'>實(shí)現(xiàn)</b>

    Linux中epoll IO多路復(fù)用機(jī)制

    epoll 是Linux內(nèi)核中的一種可擴(kuò)展IO事件處理機(jī)制,最早在 Linux 2.5.44內(nèi)核中引入,可被用于代替POSIX select 和 poll 系統(tǒng)調(diào)用,并且在具有大量應(yīng)用程序請求時(shí)能夠
    發(fā)表于 05-16 16:07 ?779次閱讀
    Linux中<b class='flag-5'>epoll</b> IO多路復(fù)用機(jī)制

    深度剖析Linux的epoll機(jī)制

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

    一文詳解epoll實(shí)現(xiàn)原理

    本文以四個(gè)方面介紹epoll實(shí)現(xiàn)原理,1.epoll的數(shù)據(jù)結(jié)構(gòu);2.協(xié)議棧如何與epoll通信;3.epoll線程安全如何加鎖;4.ET與
    的頭像 發(fā)表于 08-01 13:28 ?4436次閱讀

    epoll實(shí)現(xiàn)多路復(fù)用

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

    epoll實(shí)現(xiàn)原理

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

    epoll的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

    一、epoll的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu) 在開始研究源代碼之前,我們先看一下 epoll 中使用的數(shù)據(jù)結(jié)構(gòu),分別是 eventpoll、epitem 和 eppoll_entry。 1、eventpoll 我們
    的頭像 發(fā)表于 11-10 10:20 ?1160次閱讀
    <b class='flag-5'>epoll</b>的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

    epoll源碼分析

    個(gè)函數(shù)進(jìn)行源碼分析。 源碼來源 由于epoll實(shí)現(xiàn)內(nèi)嵌在內(nèi)核中,直接查看內(nèi)核源碼的話會有一些無關(guān)代碼影響閱讀。為此在GitHub上寫的簡化版TCP/IP協(xié)議棧,里面實(shí)現(xiàn)epoll
    的頭像 發(fā)表于 11-13 11:49 ?1421次閱讀
    <b class='flag-5'>epoll</b>源碼分析