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)