eventloop 模块总结

1. epoll介绍

1.1 函数介绍

epoll_create函数

#include <sys/epoll.h>
 
int epoll_create(int size);
 
参数:
size:目前内核还没有实际使用,只要大于0就行
 
返回值:
返回epoll文件描述符

epoll_ctl函数

#include <sys/epoll.h>
 
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
 
参数:
epfd:epoll文件描述符
op:操作码
EPOLL_CTL_ADD:插入事件
EPOLL_CTL_DEL:删除事件
EPOLL_CTL_MOD:修改事件
fd:事件绑定的套接字文件描述符
events:事件结构体
 
返回值:
成功:返回0
失败:返回-1

epoll_event 

#include <sys/epoll.h>
 
struct epoll_event{
  uint32_t events; //epoll事件,参考事件列表 
  epoll_data_t data;
} ;

typedef union epoll_data {  
    void *ptr;  
    int fd;  //套接字文件描述符
    uint32_t u32;  
    uint64_t u64;
} epoll_data_t;

头文件:<sys/epoll.h>
 
enum EPOLL_EVENTS
{
    EPOLLIN = 0x001, //读事件
    EPOLLPRI = 0x002,
    EPOLLOUT = 0x004, //写事件
    EPOLLRDNORM = 0x040,
    EPOLLRDBAND = 0x080,
    EPOLLWRNORM = 0x100,
    EPOLLWRBAND = 0x200,
    EPOLLMSG = 0x400,
    EPOLLERR = 0x008, //出错事件
    EPOLLHUP = 0x010, //出错事件
    EPOLLRDHUP = 0x2000,
    EPOLLEXCLUSIVE = 1u << 28,
    EPOLLWAKEUP = 1u << 29,
    EPOLLONESHOT = 1u << 30,
    EPOLLET = 1u << 31 //边缘触发
  };

epoll_wait函数

epoll_wait用于监听套接字事件,可以通过设置超时时间timeout来控制监听的行为为阻塞模式还是超时模式。

#include <sys/epoll.h>
 
int epoll_wait(int epfd, struct epoll_event *events,              
int maxevents, int timeout);
 
参数:
epfd:epoll文件描述符
events:epoll事件数组,如果有事件会写入这个数组里面
maxevents:epoll事件数组长度
timeout:超时时间
小于0:一直等待
等于0:立即返回
大于0:等待超时时间返回,单位毫秒
 
返回值:
小于0:出错
等于0:超时
大于0:返回就绪事件个数

2 eventloop介绍

事件循环(event loop)就是 任务在主线程不断进栈出栈的一个循环过程。任务会在将要执行时进入主线程,在执行完毕后会退出主线程。在Linux中创建一个事件循环(Event Loop)的基本思想就是在程序运行期间保持一个无限运行的循环,这个循环可以监听并响应不同的事件,例如I/O操作,定时器,信号等等。

3 eventloop 模块实现

3.1 fd_event的封装

fd_event.h

#ifndef SHLIU_NET_FDEVENT_H
#define SHLIU_NET_FDEVENT_H

#include <functional>
#include <sys/epoll.h>

namespace shliu{

class FdEvent{
public:
    enum TriggerEvent{
        IN_EVENT = EPOLLIN, //读事件
        OUT_EVENT = EPOLLOUT,//写事件
    };
    FdEvent(int fd);
    ~FdEvent();

    std::function<void()> handler(TriggerEvent event_type); //返回事件的回调
    void listen(TriggerEvent event_type,std::function<void()> callback);//添加事件,设回调

    int getFd() const { return m_fd;}

    epoll_event getEpollEvent() const { return m_listen_events; }

protected:
    int m_fd {-1};

    epoll_event m_listen_events; //监听的事件
    std::function<void()> m_read_callback;
    std::function<void()> m_write_callback;
};
    
}

#endif

fd_event.cc

#include <string.h>
#include "shliu/net/fd_event.h"
#include "shliu/common/log.h"

namespace shliu{

FdEvent::FdEvent(int fd) : m_fd(fd){
    memset(&m_listen_events,0,sizeof(m_listen_events));
}


FdEvent::~FdEvent(){

}

std::function<void()> FdEvent::handler(TriggerEvent event){
    if(event == TriggerEvent::IN_EVENT){
        return m_read_callback;
    }else{
        return m_write_callback;
    }

}

void FdEvent::listen(TriggerEvent event_type,std::function<void()> callback){
    if(event_type == TriggerEvent::IN_EVENT){
        m_listen_events.events|=EPOLLIN;
        m_read_callback = callback;
    }else{
        m_listen_events.events|=EPOLLOUT;
        m_write_callback = callback;
    }
    m_listen_events.data.ptr = this;

}

}

 3.2 wake_up_fd的封装

wakeup_fd_event.h

#ifndef SHLIU_NET_WAKEUP_FDEVENT_H
#define SHLIU_NET_WAKEUP_FDEVENT_H

#include "shliu/net/fd_event.h"

namespace shliu {

class WakeUpFdEvent : public FdEvent {
public:
  WakeUpFdEvent(int fd);

  ~WakeUpFdEvent();

  void wakeup();//写入 模拟事件发生

private:

};
}

#endif

wakeup_fd_event.cc

#include <unistd.h>
#include "shliu/net/wakeup_fd_event.h"
#include "shliu/common/log.h"


namespace shliu {

WakeUpFdEvent::WakeUpFdEvent(int fd) : FdEvent(fd) {
}

WakeUpFdEvent::~WakeUpFdEvent() {

}

void WakeUpFdEvent::wakeup() {
  char buf[8] = {'a'};
  int rt = write(m_fd, buf, 8);
  if (rt != 8) {
    ERRORLOG("write to wakeup fd less than 8 bytes, fd[%d]", m_fd);
  }
  DEBUGLOG("success read 8 bytes");
}


}

3.3 eventloop

eventloop.h

#ifndef SHLIU_NET_EVENTLOOP_H
#define SHLIU_NET_EVENTLOOP_H

#include<pthread.h>
#include<set>
#include<functional>
#include <queue>

#include "shliu/common/mutex.h"
#include "shliu/net/fd_event.h"
#include "shliu/net/wakeup_fd_event.h"

namespace shliu{

class EventLoop{
public:
    EventLoop();
    ~EventLoop();

    void loop();

    void wakeup();

    void stop();

    void addEpollEvent(FdEvent* event);
    void delEpollEvent(FdEvent* event);

    //是否有其他线程添加
    bool isInLoopThread();
    
    void addTask(std::function<void()> cb,bool is_wake_up = false);
    
private:
    void dealWakeup();
    void initWakeUpFdEvent();

private:
    pid_t m_thread_id {0}; //线程号
    std::set<int> m_listen_fds;//监听集合
    int m_epoll_fd {0};
    int m_wakeup_fd {0};
    bool m_stop_flag {false};
    std::queue<std::function<void()>> m_pending_tasks;
    WakeUpFdEvent* m_wakeup_fd_event {NULL};
    Mutex m_mutex;


};

}
#endif

eventloop.cc

#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <string.h>


#include "shliu/net/eventloop.h"
#include "shliu/common/log.h"
#include "shliu/common/util.h"

#define ADD_TO_EPOLL()\
    auto it = m_listen_fds.find(event->getFd());\
    int op = EPOLL_CTL_ADD;\
    if(it != m_listen_fds.end()){\
        op = EPOLL_CTL_MOD;\
    }\
    epoll_event tmp = event->getEpollEvent();\
    INFOLOG("epoll_event.events = %d", (int)tmp.events);\
    int rt = epoll_ctl(m_epoll_fd,op,event->getFd(),&tmp);\
    if(rt== -1){\
        ERRORLOG("epoll_ctl errorwhen add fd ,errno =%d,error=%s",errno,strerror(errno));\
    }\
    DEBUGLOG("add event success,fd[%d]",event->getFd());\

#define DEL_TO_EPOLL()\
    auto it = m_listen_fds.find(event->getFd());\
    int op = EPOLL_CTL_DEL;\
    if(it != m_listen_fds.end()){\
        return;\
    }\
    epoll_event tmp = event->getEpollEvent();\
    int rt = epoll_ctl(m_epoll_fd,op,event->getFd(),&tmp);\
    if(rt== -1){\
        ERRORLOG("epoll_ctl error when add fd ,errno =%d,error=%s",errno,strerror(errno));\
    }\
    DEBUGLOG("del event success,fd[%d]",event->getFd());\

namespace shliu{
    
static thread_local EventLoop* t_current_eventloop = NULL;
static int g_epoll_max_timeout = 10000;
static int g_epoll_max_events = 10;

EventLoop::EventLoop(){
    if(t_current_eventloop!=NULL){
        ERRORLOG("failed to create EventLoop,this thread has created EventLoop");
        exit(0);
    }

    m_thread_id = getThreadId();

    m_epoll_fd = epoll_create(10);
    if(m_epoll_fd == -1){
        ERRORLOG("epoll_create error,error[%d]",errno);
        exit(0);
    }
    initWakeUpFdEvent();//添加唤醒的fd

    INFOLOG("successfully created EventLoop %d",m_thread_id);

    t_current_eventloop = this;

}
EventLoop::~EventLoop(){
    close(m_epoll_fd);
    if(m_wakeup_fd_event){
        delete m_wakeup_fd_event;
        m_wakeup_fd_event = NULL;
    }

}

void EventLoop::initWakeUpFdEvent() {
  m_wakeup_fd = eventfd(0, EFD_NONBLOCK);
  if (m_wakeup_fd < 0) {
    ERRORLOG("failed to create event loop, eventfd create error, error info[%d]", errno);
    exit(0);
  }
  INFOLOG("wakeup fd = %d", m_wakeup_fd);

  m_wakeup_fd_event = new WakeUpFdEvent(m_wakeup_fd);

  m_wakeup_fd_event->listen(FdEvent::IN_EVENT, [this]() {
    char buf[8];
    while(read(m_wakeup_fd, buf, 8) != -1 && errno != EAGAIN) {
    }
    DEBUGLOG("read full bytes from wakeup fd[%d]", m_wakeup_fd);
  });

  addEpollEvent(m_wakeup_fd_event);

}   

void EventLoop::loop(){
    while(!m_stop_flag){ 
        Mutex::Lock lock(m_mutex);
        std::queue<std::function<void()>> tmp_tasks;
        m_pending_tasks.swap(tmp_tasks);
        lock.unlock();

        while(!tmp_tasks.empty()){
            std::function<void()>cb = tmp_tasks.front();
            tmp_tasks.pop();
            if(cb){
                DEBUGLOG("start to cb ");
                cb();
            }
        }
        int timeout = g_epoll_max_timeout;
        epoll_event result_events[g_epoll_max_events];

        //DEBUGLOG("now begin to epoll wait");
        int rt = epoll_wait(m_epoll_fd,result_events,g_epoll_max_events,timeout);
        DEBUGLOG("now end epoll wait,rt = %d",rt);

        if(rt<0){
            ERRORLOG("epoll_wait error,errno =%d",errno);
        }else{
            for(int i=0;i<rt;++i){
                //取出事件
                epoll_event trigger_event = result_events[i];
                FdEvent* fd_event = static_cast<FdEvent*>(trigger_event.data.ptr);
                if(fd_event==NULL){
                    continue;
                }
                //read
                if(trigger_event.events & EPOLLIN){
                    DEBUGLOG("fd %d trigger EPOLLIN evnet",fd_event->getFd());
                    addTask(fd_event->handler(FdEvent::IN_EVENT));
                }
                //write
                if(trigger_event.events & EPOLLOUT){
                    DEBUGLOG("fd %d trigger EPOLLOUT evnet",fd_event->getFd());
                    addTask(fd_event->handler(FdEvent::OUT_EVENT));
                }

            }
        }


    }

}

void EventLoop::wakeup(){
    m_wakeup_fd_event->wakeup();
}

void EventLoop::stop(){
    m_stop_flag = true;
}

void EventLoop::dealWakeup(){}

void EventLoop::addEpollEvent(FdEvent* event){
    if(isInLoopThread()){//是当前线程直接添加
        ADD_TO_EPOLL();
    }else{//不是 把函数设置到回调里面
        auto cb = [this,event](){
           ADD_TO_EPOLL(); 
        };
        addTask(cb,true);
    }

}
void EventLoop::delEpollEvent(FdEvent* event){
    if(isInLoopThread()){//是当前线程直接添加
        DEL_TO_EPOLL();
    }else{//不是 把函数设置到回调里面
        auto cb = [this,event](){
           DEL_TO_EPOLL(); 
        };
        addTask(cb,true);
    }

}

bool EventLoop::isInLoopThread(){
    return getThreadId() == m_thread_id;

}

void EventLoop::addTask(std::function<void()> cb,bool is_wake_up /*=fasle*/){
    Mutex::Lock lock(m_mutex);
    m_pending_tasks.push(cb);
    lock.unlock();
    if(is_wake_up) {
        wakeup();
    }

}

}

4I/O模块

#ifndef SHLIU_NET_IO_THREAD_H
#define SHLIU_NET_IO_THREAD_H

#include <pthread.h>
#include <semaphore.h>
#include "shliu/net/eventloop.h"

namespace shliu{

class IOThread{
public:
    IOThread();
    ~IOThread();

    EventLoop* getEventLoop() const {return m_event_loop;}

public:
    static void* Main(void* arg);
    void start();
    void join();

private:
    pid_t m_thread_id {-1};     //线程ID
    pthread_t m_thread {0};   //线程句柄
    EventLoop* m_event_loop {NULL}; 
    sem_t m_init_semaphore; //信号量
    sem_t m_start_semaphore;
};

}

#endif

#include <pthread.h>
#include <assert.h>

#include "shliu/common/log.h"
#include "shliu/net/io_thread.h"
#include "shliu/common/util.h"

namespace shliu{

IOThread::IOThread(){
    int rt = sem_init(&m_init_semaphore,0,0);
    assert(rt==0);

    rt = sem_init(&m_start_semaphore, 0, 0);
    assert(rt == 0);

    pthread_create(&m_thread,NULL,&IOThread::Main,this);
    //wait 直到main函数执行到loop,同步机制
    sem_wait(&m_init_semaphore);
    DEBUGLOG("IOThread %d create success",m_thread_id);
}

IOThread::~IOThread(){
    m_event_loop->stop();
    sem_destroy(&m_init_semaphore);
    sem_destroy(&m_start_semaphore);

    pthread_join(m_thread,NULL);

    if(!m_event_loop){
        delete m_event_loop;
        m_event_loop = NULL;
    }

}

void* IOThread::Main(void* arg) {
  IOThread* thread = static_cast<IOThread*> (arg);

  thread->m_event_loop = new EventLoop();
  thread->m_thread_id = getThreadId();


  // 唤醒等待的线程void IOThread::start() {
  sem_post(&thread->m_init_semaphore);

  // 让IO 线程等待,直到我们主动的启动
  DEBUGLOG("IOThread %d created, wait start semaphore", thread->m_thread_id);

  sem_wait(&thread->m_start_semaphore);
  DEBUGLOG("IOThread %d start loop ", thread->m_thread_id);
  thread->m_event_loop->loop();

  DEBUGLOG("IOThread %d end loop ", thread->m_thread_id);

  return NULL;

}

void IOThread::start() {
  DEBUGLOG("Now invoke IOThread %d", m_thread_id);
  sem_post(&m_start_semaphore);
}

void IOThread::join(){
    pthread_join(m_thread,NULL);
}

}

参考:

Gooddbird/rocket: c++ rpc framework, simplified version of tinyrpc。 (github.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值