在上一篇中,我们主要讨论了Windows下关于完成端口的一些知识.对应于完成端口,Linux下面在2.5.44内核中有了epoll,这个是为处理大批量句柄而引进的.
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
先来看看为什么要引进epoll以及它带来的好处.
在Linux内核中,原有的select所用到的FD_SET是有限的,在内核中的参数_FD_SETSIZE来设置的.如果想要同时检测1025个句柄的可读(或可写)状态,则select无法满足.,select是采用轮询方法进行检测的,也就是说每次检测都要遍历所有FD_SET中的句柄.显然,当随着FD_SET中的句柄数的增多,select的效率会不断的下降.如今的服务器,都是要满足上万甚至更多的连接的,显然想要更高效的实现这一要求,采用新的方法.于是,不断的修改后,终于形成了稳定的epoll.
epoll优点:(1)支持大数量的socket描述符(FD).举个例子来说,在1GB内存的机器上大约可以打开10万个左右的socket,这个数字足以满足一般的服务器需求.(2)epoll的IO效率不会随着FD数量的增加而线性下降(多少肯定会下降的).至于原理,可以查阅epoll的实现原理;(3)使用mmap加速内核与用户空间的消息传递,无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的.当然epoll还有其他一些优点,这里就不一一列举了.
下面来重点说说epoll的使用,这也是大家最关心的部分.在2.6内核中epoll变的简洁而强大.
先来弄清楚一个概念,即epoll的2种工作方式:LT和ET.
LT(level triggered)是缺省的工作方式,同时支持block和non-block.其实这个有点像电路里面的电平触发方式.在这种模式下,内核会告诉你一个文件描述符fd就绪了,然后你就可以对这个fd进行IO操作.如果你不做任何操作,内核会继续通知你.,假如你读取数据没有读取完时,内核会继续通知你.其实传统的select/poll就是这种模式.
ET(edge-triggered)是告诉工作方式,只支持non-block.其实这个有点像电路里面的边沿触发方式.这个是高效服务器必选的方式.在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll通知你.然后对这个fd只通知你一次,之后一直为就绪态,没有了状态的变化,直到你做了某些操作导致了那个fd不再为就绪态.但是注意,如果一直不对这个fd进行IO操作,内核不会发送更多的通知.
在弄清楚了上述两种模式之后,接下来就可以使用epoll了.主要用到三个函数epoll_create,epoll_ctl,epoll_wait.
int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大.当创建好epoll句柄后,它就是会占用一个fd值,在使用完epoll后,调用close()关闭,否则可能导致fd被耗尽.
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中; EPOLL_CTL_MOD:修改已经注册的fd的监听事件; EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:,这样不断的读和发,当缓冲区满后会产生EAGAIN错误,同时不理会这次请求发送的数据.这时就需要你对send进行一些改动,等待片刻后继续发送,并检查看发送的数据量是否正确.
|