进入2.6内核时代,select应该进垃圾堆了
高并发服务器用select效率极低,特别是使用非阻塞IO时更是慢得一蹋糊涂
改用epoll会大大改善
我一个程序监听从8000到18000共计1万个端口,启动1万个LISTEN
用epoll来阻塞,系统非常轻松,完全没有惊群现象

epoll用法比select简单

初始化:创建epoll描述字;向epoll描述字添加需要响应的套接字,初始化过程只要一次即可

使用:等待epoll事件发生,提取事件的套接字进行相应的读写操作


staticints_epfd;//epoll描述字

{//初始化epoll
structepoll_eventev;

//设置epoll
s_epfd=epoll_create(65535);

{//这个过程可以循环以便加入多个LISTEN套接字进入epoll事件集合
//服务器监听创建
rc=listen();//listen参数这里省略

//加入epoll事件集合
ev.events=EPOLLIN;
ev.data.fd=rc;
if(epoll_ctl(s_epfd,EPOLL_CTL_ADD,rc,&ev)<0){
fprintf(stderr,"epollsetinsertionerror:fd=%d",rc);
return(-1);
}
}
}

{//epoll事件处理
inti,nfds,sock_new;
structepoll_eventevents[16384];
for(;;){
//等待epoll事件
nfds=epoll_wait(s_epfd,events,16384,-1);
//处理epoll事件
for(i=0;i<nfds;i++){
//events.data.fd是epoll事件中弹出的套接字
//接收连接
sock_new=accept(events.data.fd);//accept其它参数这里省略了
if(0>sock_new){
fprintf(stderr,"接收客户端连接失败\n");
continue;
}
}
}
}

1、为什么select是落后的?

首先,在Linux内核中,select所用到的FD_SET是有限的,即内核中有个参数__FD_SETSIZE定义了每个FD_SET的句柄个数,在我用的2.6.15-25-386内核中,该值是1024,搜索内核源代码得到:

include/linux/posix_types.h:#define__FD_SETSIZE1024

也就是说,如果想要同时检测1025个句柄的可读状态是不可能用select实现的。或者同时检测1025个句柄的可写状态也是不可能的。

其次,内核中实现select是用轮询方法,即每次检测都会遍历所有FD_SET中的句柄,显然,select函数执行时间与FD_SET中的句柄个数有一个比例关系,即select要检测的句柄数越多就会越费时。

当然,在前文中我并没有提及poll方法,事实上用select的朋友一定也试过poll,我个人觉得select和poll大同小异,个人偏好于用select而已。



/************关于本文档********************************************

*filename:Linux2.6内核中提高网络I/O性能的新方法epoll

*purpose:补充“Linux下各类TCP网络服务器的实现源代码”一文的不足之处

*wroteby:zhoulifa(zhoulifa@163.com)周立发(http://zhoulifa.bokee.com)

Linux爱好者Linux知识传播者SOHO族开发者最擅长C语言

*datetime:2006-07-0622:30:00

*Note:任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

*但请遵循GPL

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

*********************************************************************/



2、2.6内核中提高I/O性能的新方法epoll



epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。要使用epoll只需要这三个系统调用:epoll_create(2),epoll_ctl(2),epoll_wait(2)。

当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4)isanewAPIintroducedinLinuxkernel2.5.44)



以下文章转自滕昱的WebLoghttp://mechgouki.spaces.msn.com/blog/PersonalSpace.aspx
[QUOTE]

/*********************************引用开始******************************/


[版权声明]:此文档遵循GNU自由文档许可证(GNUFreeDocumentationLicense).任何人可以自由复制,分发,修改,不过如果方便,请注明出处和作者:)



(1)导言:



首先,我强烈建议大家阅读RichardStevens著作《TCP/IPIllustractedVolume1,2,3》和《UNIXNetworkProgrammingVolume1,2》。虽然他离开我们大家已经5年多了,但是他的书依然是进入网络编程的最直接的道路。其中的3卷的《TCP/IPIllustracted》卷1是必读-如果你不了解tcp协议各个选项的详细定义,你就失去了优化程序重要的一个手段。卷2,3可以选读一下。比如卷2讲解的是4.4BSD内核TCP/IP协议栈实现----这个版本的协议栈几乎影响了现在所有的主流os,但是因为年代久远,内容不一定那么vogue.在这里我多推荐一本《TheLinuxNetworkingArchitecture--DesignandImplementationofNetworkProtocolsintheLinuxKernel》,以2.4内核讲解LinuxTCP/IP实现,相当不错.作为一个现实世界中的实现,很多时候你必须作很多权衡,这时候参考一个久经考验的系统更有实际意义。举个例子,linux内核中sk_buff结构为了追求速度和安全,牺牲了部分内存,所以在发送TCP包的时候,无论应用层数据多大,sk_buff最小也有272的字节.



其实对于socket应用层程序来说,《UNIXNetworkProgrammingVolume1》意义更大一点.2003年的时候,这本书出了最新的第3版本,不过主要还是修订第2版本。其中第6章《I/OMultiplexing》是最重要的。Stevens给出了网络IO的基本模型。在这里最重要的莫过于select模型和AsynchronousI/O模型.从理论上说,AIO似乎是最高效的,你的IO操作可以立即返回,然后等待os告诉你IO操作完成。但是一直以来,如何实现就没有一个完美的方案。最著名的windows完成端口实现的AIO,实际上也是内部用线程池实现的罢了,最后的结果是IO有个线程池,你应用也需要一个线程池......很多文档其实已经指出了这带来的线程context-switch带来的代价。



在linux平台上,关于网络AIO一直是改动最多的地方,2.4的年代就有很多AIO内核patch,最著名的应该算是SGI那个。但是一直到2.6内核发布,网络模块的AIO一直没有进入稳定内核版本(大部分都是使用用户线程模拟方法,在使用了NPTL的linux上面其实和windows的完成端口基本上差不多了)。2.6内核所支持的AIO特指磁盘的AIO---支持io_submit(),io_getevents()以及对DirectIO的支持(就是绕过VFS系统buffer直接写硬盘,对于流服务器在内存平稳性上有相当帮助)。



所以,剩下的select模型基本上就是我们在linux上面的唯一选择,其实,如果加上no-blocksocket的配置,可以完成一个"伪"AIO的实现,只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受的缺点,所以改进一直是2.4-2.5开发版本内核的任务,包括/dev/poll,realtimesignal等等。最终,DavideLibenzi开发的epoll进入2.6内核成为正式的解决方案