epoll学习(一)

来源:互联网 发布:网店美工毕业论文 编辑:程序博客网 时间:2024/05/18 00:42

Epoll是当前Linux下开发大规模并发程序设计的热门选择,EpollLinux2.6内核引入,和select相似,其实都是I/O多路复用技术。

select , poll 模型:
1:最大并发数限制,因为一个进程打开文件描述符是有限制的,由FD_SETSIZE限制。

2:效率问题,select每次调用都会线性扫描全部的文件描述符集合,这样效率就会线性下降。

3:内核/用户空间内存拷贝问题,select采用内存拷贝方法。

Epoll :

1:epoll支持的文件描述符上限是最大可以打开文件数目,这个数字远大于2048,具体数目可到cat /proc/sys/fs/file-max查看。

2:效率提升,Epoll最大的优点它只管活跃的连接,而跟连接总数无关,,因此在实际网络,Epoll效率远高于selectpoll

3:使用mmap加速内核与用户空间的消息传递,通过内核与用户空间映射同一块内存实现。 

select每次调用时都要传递你所要监控所有socketselect系统调用,这意味着将用户态socket列表cpoy到内核态,如果很多句柄会导致每次都要copy几十或几百kb内存到内核,非常低效,而我们调用epoll时就相当于以往调用select/poll,但这时并不传递socket句柄给内核,因为内核已经在epoll_ctl()拿到了要监控的列表。所以在调用epoll_create后内存已经帮你储存要监控的描述符,每次调用epoll_ctl只是往数据结构添加新的描述符,调用epoll_wait收集发生事件的连接。

epoll相关系统调用:

epoll只有epoll_createepoll_ctlepoll_wait三个系统调用。

1. int epoll_create (int size) ;  epoll返回一个句柄,之后的epoll使用都将依靠这个句柄标识,参数size是告诉epoll所要处理大致事件数目,不再使用时,要close(),在Linux最新的内核版本size无任何意义。

2. int epoll_ctl (int epfd , int op , int fd , struct epoll_event *event) ;  向epoll对象添加、修改或删除事件,成功返回0,失败返回-1,第一个参数是epoll_create()返回值,第二个参数表示动作,由三个宏表示:

EPOLL_CTL_ADD: 注册新的fd事件到epoll中。

EPOLL_CTL_MOD:修改已经注册的epoll事件。

EPOLL_CTL_DEL:从epfd删除一个fd

第三个参数是需要监听连接套接字。第四个参数是告诉epoll对什么样的事件监听,它使用了struct epoll_event

struct epoll_event {

__uint32_t events ; //监听事件类型

epoll_data_t data ; //epoll_data集合

} ;

typedef  union epoll_data {

void *ptr ;

int fd ;

uint32_t u32 ;

uint64_t u64 ;

} epoll_data_t ;

events可以是以下几个宏组合:

EPOLLIN:表示对应的文件描述符可读。

EPOLLOUT:表示对应的文件描述符可写。

EPOLLPRI:表示对应的文件描述符有紧急数据可读。

EPOLLHUP:表示对应的文件描述符挂断。

EPOLLET:将触发方式设置为边缘出发(ET),默认为水平触发(LT)。

EPOLLNESHOT:表示这个事件只处理一次,下次重新处理时需重新加入epoll

int epoll_waitint epfd , struct epoll_event *events , int maxevents , int timeout

收集在epoll监控的事件已经发生的事件,如果epoll中没有任何一个事件发生,则最多等待timeout毫秒后返回,返回值表示当前发生事件个数,出错返回-1,第二个参数events是已经分配好空间的epoll_event结构体数组,epoll会把已经发生事件复制到events数组(events不可以是空指针,内核负责把事件相关信息复制到events数组,不会去帮助我们在用户态中分配内存)maxevents表示本次可以返回的最大事件数目,每次能处理的事件数,通常与预分配events数组大小相等,不能大于epoll_create()时的size

epoll工作:epoll只通知那些就绪的FD,当调用epoll_wait()获得这些就绪FD,返回一个就绪描述符数量的值,用户只需去epoll指定的一个events数组获取相应位置的描述符,使用了内存映射mmap方法,节省系统调用开销。另外改进是epoll采用基于事件通知的就绪通知方式,当某个文件描述符就绪时,内核就会采用callback回调机制,当进程调用epoll_wait()就会得到通知。

epoll有两种工作模式,LT(水平模式)和ET(边缘模式)。假如有这样一个例子:

1.我们已经把一个用来读取数据的FD添加到epoll描述符

2.这时候管道从另一段写入2KB数据

3.调用epoll_wait(),并且他会返回FD,说明他已经准备好读数据

4.然后读取1K数据

5.调用epoll_wait()。。。。。。。

边缘工作模式(ET):

如果我们第一步把FD添加到epoll描述符的时候使用EPOLLET标志,那么在最后调用epoll_wait()后将有可能挂起,因为剩余的数据还存在于文件的输入缓冲区中,而且数据发出端还在等待一个针对已发出数据的反馈信息,只有在监视文件FD发生某个事件的时候ET工作模式才会报告此事件。因为在第5步,调用者可能会放弃等待仍存在于数据缓冲区的剩余数据。在上面例子中,会有一个事件产生在文件FD上,因为第2步执行写操作,然后事件将在第3步摧毁,因为第4步的读取操作没有读空文件缓冲区数据,因此在第5步调用epoll_wait()后是否不一定挂起,epoll工作在ET模式,必须使用非阻塞套接口,以避免由于一个文件描述符的阻塞读/写操作把多个描述符操作饿死,最好以下面方式调用ET模式的epoll接口 1:基于非阻塞的文件描述符 2:只有当read()或write()返回EAGAIN才需要挂起,等待。但这并不是说每次read()都需要循环读,直到读到产生一个EAGAIN才认为此事件处理完成,当read()返回读到数据长度小于请求数据长度时,就可以确定此缓冲区没有数据,此事件已经处理完成。

水平工作模式(LT):

缺省工作模式,并且同时支持blocknon-block状态,内核会告诉你一个文件描述符是否就绪,然后对就绪的fd进行IO操作,如果不做任何操作,内核还会通知,所以这种模式出错可能性较小。

ET模式的效率要比LT模式效率要高,它只支持非阻塞套接字。ET模式和LT模式区别在于,当一个新的时间到来时,ET模式当然可以从epoll_wait()获取到该事件,可是如果这次没有把这事件对应套接字缓冲区处理完,在这个套接字没有新的事件到来时,ET模式无法从epoll_wait调用中获取事件,而LT相反,只要一个事件对应套接字缓冲区还有数据,就能从epoll_wait获取这个事件。因此,在LT模式下开发epoll应用简单,而在ET模式下事件发生时,如果没有彻底将缓冲区数据处理完,而会导致缓冲区内的用户请求得不到响应,默认情况,Nginx通过ET使用epoll


0 0
原创粉丝点击