select、epoll简介

来源:互联网 发布:天下网商招聘php 编辑:程序博客网 时间:2024/05/17 23:56

一.select模型

select告诉内核对哪些描述符感兴趣及等待多长时间,指示内核在有一个或多个事件发生或经历一段指定时间后才唤醒它,以返回值的不同来反映函数的执行情况。

fd_set fdset;
fd_set是一个存放文件描述符的集合,以下宏可以对该队列进行操作:
FD_ZERO(&fdset); 将fdset清空,使集合中不含任何fd
FD_SET(int fd, &fdset): 将fd加入fdset集合
FD_ISSET(int fd, &fdset): 检查fdset集合中的描述符fd是否可读写
FD_CLR(int fd, &fdset):将fd从fdset集合中删除

int select( int nfds, fd_set readfds, fd_set *writefds, fd_set exceptfds, const struct timeval &timeout);
参数说明:
nfds:一般设置为最大fd的值加1
readfds: 监听fd集合是否可读
writefds: 监听fd集合是否可行
exceptfds: 监听异常事件
timeout: 设置超时时间
struct timeval {
long tv_sec; //秒
long tv_usec; //毫秒
};
如果timeout设置为NULL:select阻塞状态,等待直到监听的事件发生
设置为0(tv_sec=0; tv_usec=0):立即返回
设置为某个时间值:等待一段时间返回,有事件就返回,否则超时返回。
返回值:
负值:select错误
正值:某些文件可读写或异常
0:等待超时

缺点:
1.最大并发数限制,因为一个进程所打开的fd(文件描述符)是有限制的,由FD_SETSIZE设置,默认值为1024/2048
2.效率问题,select每次调用都会线性扫描全部的fd集合。
3.每次调用要重复的从用户态读入参数,内存拷贝问题,比较费时。

二、epoll模型

1.epoll没有最大并发链接的限制,上限是最大可以打开的数目
2.epoll只管“活跃”的链接,跟链接总数无关。
3.使用“共享内存”,省略了内存拷贝。

epoll接口就三个函数:
1. int epoll_create(int size)
创建一个epoll的句柄
2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
epoll的事件注册函数
epfd:epoll_create()的返回值
op: EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
fd:需要监听的fd
event:
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
typedef union epoll_data
{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

events可以是以下几个宏的集合:
EPOLLIN : 表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT: 表示对应的文件描述符可以写;
EPOLLPRI: 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR: 表示对应的文件描述符发生错误;
EPOLLHUP: 表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT: 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

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

等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

EPOLL事件有两种模型:

Edge Triggered (ET) 边缘触发 只有数据到来,才触发,不管缓存区中是否还有数据。
Level Triggered (LT) 水平触发 只要有数据都会触发。

LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.
用LT这种默认模式,不容易出错,只要缓存里的数据没有处理完,就会一直通知。

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认(这句话不理解)。
用ET模式的话,对于客户发送的数据只通知一次,如果这次没有处理完,不会继续通知,而是等下次客户发数据进行触发,这样数据也会累计,而且会影响其他客户的请求连接,导致阻塞。

0 0
原创粉丝点击