多路转接之epoll
一、epoll模型的底层原理
1.数据从外设到cpu的过程
网络中的数据先到网卡外设,数据到达网卡,网卡会向cpu发送硬件中断,cpu根据中断信号在中断向量表找到中断对应的中断处理函数方法,这个函数方法就会让网卡驱动程序将数据从网卡拷贝到内存,cpu执行指令时再从内存中读取数据。
2.epoll底层的数据结构
epoll是操作系统提供的系统调用,操作系统在创建epoll时,维护了红黑树,每个红黑树结点都包含了用户告诉操作系统需要监视的文件描述符的事件。一个结点对应一个描述符的一个事件,每个结点包含(int sockfd,uint32_t events)这些信息。
操作系统在创建epoll时还创建了一个就绪队列,就是一个双向链表,每个链表结点包含(int sockfd,uint32_t events)这些信息,就绪队列中存储事件就绪的文件描述符和事件,就绪队列作用就是操作系统内核告诉用户哪些文件描述符的哪些事件就绪了。
数据从网卡经过网络协议栈到达文件的缓冲区,此时文件缓冲区有数据就绪,文件中有对应的回调函数(void*private_data)会将红黑树中的文件描述符事件对应的结点加入就绪队列中。
红黑树,双向链表以及回调方法组成了epoll模型。这个epoll模型被放在一个struct file文件中,所以epoll模型也有自己的文件描述符,epoll模型对应的文件描述符被放在进程PCB中的文件描述符表中所管理。
3.epoll的两种工作模式
epoll的两种工作模式:LT(Level Triggered)水平触发、ET(Edge Triggered)边缘触发。
LT:只要传输层的接受缓冲区里面有数据,epoll就会一直通知用户读取数据。或者传输层的发送缓冲区有剩余空间,就一直通知用户写数据。
ET:文件描述符对应的事件就绪,epoll只会通知用户一次。接受缓冲区有数据到来,会通知用户一次,如果接受缓冲区里的数据没有读取完,epoll就不会通知用户读取数据,除非接受缓冲区的数据增加了。同理发送缓冲区有空间,操作系统内核只通知用户一次。
select,epoll默认工作模式是LT。
ET模式下文件描述符必须是非阻塞的,因为为了保证将接受缓冲区的数据读完,必须采用循环读取的策略,当读到没有数据时,如果文件描述符是阻塞的,那么进程就会被阻塞。所以必须保证文件描述符是非阻塞的,在非阻塞状态下,若文件描述符对应的文件中没有数据,recv会返回-1,errno会被设置成EAGAIN或EWOULDBLOCK。
LT模式下文件描述符既可以是阻塞的也可以是非阻塞的,当是非阻塞的时候和ET模式工作效率没有区别。
ET比LT更高效是因为ET只通知一次,用户必须将接受缓冲区的数据全部取走,或者将数据写入发送缓冲区直到数据写完或者没有空间。将接受缓冲区的数据全部取走,这样可以通过tcp向对方发送更大的窗口大小,对方的滑动缓冲区根据窗口大小也会变大,滑动缓冲区变大就能一次型发送更多的数据。从而提高网络通信的效率。
TCP中的PSH类型报文,就是将数据就绪事件再次让用户知道。
二、认识epoll
1.epoll_create
#include <sys/epoll.h>
int epoll_create(int size);
epoll_create:创建epoll模型。
size必须大于0,返回值是epoll模型对应的文件描述符。
2.epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
epoll_ctl:对epoll模型中的红黑树做增删查改。红黑树是以文件描述符作为key值的。
用户告诉内核需要监视文件描述符的事件。
op代表用户想要怎样修改红黑树。
//op的取值
EPOLL_CTL_ADD //增加epoll监视的事件
EPOLL_CTL_MOD //修改epoll监视的某个事件
EPOLL_CTL_DEL //删除epoll事件的某个事件
3.epoll_wait
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
epoll_wait:从epoll模型中的就绪队列提取就绪文件描述符的事件。
就绪队列保证了无需检测文件描述符的事件是否就绪。
返回值表示就绪事件的数量。
events表示事件可能的取值为:
EPOLLIN:表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数可读;
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: ET的epoll工作模式;
4.epoll的优势
epoll监视文件描述符的数量没有上限,epoll无需频繁进行文件描述符数组的拷贝,epoll底层是红黑树对于文件描述的读写事件的增删查改非常方便时间复杂度低,epoll的返回值是已经有读写事件就绪的文件描述符,无需再对所有的文件描述符进行遍历找寻事件就绪的文件描述符。
三、基于epoll实现的server
main.cc
#include "epollServer.hpp"
#include <memory>
using namespace std;
using namespace epoll_ns;
std::string handle(const std::string& request){
return "I am epollServer,"+request;
}
static void usage(string proc){
cerr<<proc<<" need a port!!!!"<<endl;
}
int main(int argc,char* argv[]){
if(argc!=2){
usage(argv[0]);
exit(1);
}
uint16_t port=atoi(argv[1]);
unique_ptr<epollServer> svr(new epollServer(handle,port));
svr->init();
svr->start();
return 0;
}
epollServer.hpp
#pragma once
#include <iostream>
#include "sock.hpp"
#include "public.hpp"
#include <errno.h>
#include <cstring>
#include <functional>
#include <sys/epoll.h>
#include <functional>
using namespace std;
namespace epoll_ns{
class epollServer{
static const uint16_t defaultPort=8080;
static const int defaultfd=-1;
static const int size=128;
static const int defaultnum=64;
using func_t=std::function<std

最低0.47元/天 解锁文章
6102

被折叠的 条评论
为什么被折叠?



