概要
事件种类
epoll_event是Linux操作系统中的一个数据结构,用于表示一个I/O事件。它在使用epoll多路复用技术时作为参数传递给
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)//传入参数
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)//传出参数
以便在事件发生时通知应用程序。
epoll_event结构体的定义如下:
// 联合体, 多个变量共用同一块内存
typedef union epoll_data {
void *ptr;
int fd; // 通常情况下使用这个成员, 和epoll_ctl的第三个参数相同即可
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
// 管理红黑树上的文件描述符(添加、修改、删除)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
//检测创建的epoll实例中有没有就绪的文件描述符
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
events:委托epoll检测的事件
EPOLLIN:读事件, 接收数据, 检测读缓冲区,如果有数据该文件描述符就绪
EPOLLOUT:写事件, 发送数据, 检测写缓冲区,如果可写该文件描述符就绪
EPOLLERR:异常事件
data:用户数据变量,这是一个联合体类型,通常情况下使用里边的fd成员,用于存储待检测的文件描述符的值,在调用epoll_wait()函数的时候这个值会被传出,其中ptr可以指向任何类型的用户数据。
总的来说,epoll_event结构体是Linux系统中实现事件驱动编程的一个关键数据结构,通过它可以将多个I/O事件集中处理,提高了程序的效率和响应性。
Channel
Channel 理解为通道,封装了sockfd和其感兴趣的event,如EPOLLIN、EPOLLOUT事件,channel和event一一映射,通过channel能够获取到event中的events,即感兴趣的事件,通过channel也可以改变events感兴趣的事件。同时Poller监听,还绑定了poller返回的具体时间,
这些事件要向poller里面注册,发生事件由poller通知channel,channel收到相应fd事件通知后,调用回调操作(如果有事件发生,那么该事件所对应channel也会调用所相应发生事件的回调函数)
这就是channel类的主要作用,起到了一个代表event,但大于event作用的这样一个类。
将已连接的客户端连接 connfd,打包成了一个Cannel,其内部封装了connfd,loop_ (此Channel所在的EventLoop)、event (EPOLLIN/OUT),revent(响应的事件)和对应事件发生时的一系列回调。
muduo库中只有两种channel, 用来监听新连接的 listenfd-acceptorChannel 和客户端的 connChannel ,acceptorChannel 注册在MainLoop的Poller上。
框架与细节
成员
EventLoop *loop_;//事件循环
const int fd_;//fd,poller监听的对象
int events_;//注册fd感兴趣事件
int revents_;//poller返回的具体发生的事件
int index_;//获取channel在poller中的状态(kNew)
channel中成员包括自己所属的事件循环,还有channel对应的套接字,用来监听;以及该channel中event所感兴趣的事件,和真正监听到的事件,以及channel此时在Poller中处于什么状态,对应着Poller是否在监听对应的event
//当前fd的状态,对感兴趣事件状态信息的描述,标识作用
static const int kNoneEvent;//对任何事件不感兴趣
static const int kReadEvent;//对读事件感兴趣
static const int kWirteEvent;//对写事件感兴趣
代表感兴趣事件的类型。设为static变量,类使用。
/*
因为channel通道里能够获知fd最终发生的具体事件revents,
所以它负责调用具体事件的回调操作(根据不同事件回调)
channel不知道具体做什么样的回调,他只能被动执行你给他设定的回调,
这些回调函数都是私有的,所以提供四个对外的接口setReadCallback
*/
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
相关的回调函数
方法
//设置回调函数对象
/*
cb是左值,但是function是一个对象,占用资源很大,调用function函数对象赋值操作,是std::move
把对象的资源转给readCallback_,因为出了这个函数cb这个形参的局部对象就不需要了
*/
void setReadCallback(ReadEventCallback cb){readCallback_ = std::move(cb);}
void setWriteCallback(EventCallback cb){writeCallback_ = std::move(cb);}
void setCloseCallback(EventCallback cb){closeCallback_ = std::move(cb);}
void setErrorCallback(EventCallback cb){errorCallback_ = std::move(cb);}
//设置fd相应的事件状态,updata()通知poller调用epoll_ctl,把fd感兴趣的事件添加到epoll_ctl里
void enableReading(){events_ |= kReadEvent;update();}
void disableReading(){events_ &= ~kReadEvent;update();}
void enableWriting(){events_ |= kWirteEvent;update();}
void disableWriting(){events_ &= ~kWirteEvent;update();}
void disableAll(){events_ = kNoneEvent;update();}
//当前的channel(fd),有没有注册感兴趣的事件,返回fd当前的事件状态
bool isNoneEvent()const{return events_ == kNoneEvent;}
bool isWriting() const{return events_ & kWirteEvent;}
bool isReading() const{return events_ & kReadEvent;}
/*当前这个channel属于哪个eventloop,
//!一个线程有一个eventloop(事件循环),线程和事件循环是一一对应的,一个eventloop里面有一个poller(抽象了多路事件分发器),一个poller可以监听很多个channel,
每个channel都属于一个eventloop,但是一个eventloop可以有很多个channel(多路,既一个线程监听多个fd)
*/
EventLoop* ownerLoop(){return loop_;}
参考: