几种模型的比较
关于PPC、TPC的问题、I/O复用的由来、epoll和select的详细对比参考文章:高性能网络编程5--IO复用与并发编程
1 PPC、TPC模型
传统的网络服务器是用一个单独的线程或进程处理每一个连接。对于高性能的应用,这需要在某一个时刻同时处理大量的客户请求,这种模式效率不高,因为(Process Per Connection,PPC), TPC(Thread Per Connection)模型一次处理许多客户连接,那么随着连接客户的增多,那么资源使用、进程/线程环境切换等的时空花销就会很大。
2 select 模型
参看文章《unix网络编程》(13)select、shutdown函数 《unix网络编程》(15)poll函数以及使用poll的客户服务器程序
1) 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024,因此 Select 模型的最大并发数就被相应限制了。如果要改变FD_SIZE的大小需要重新编译内核。
int select(int maxfdp1, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
2) 效率问题, select 每次调用都会 线性扫描全部的 FD 集合,花费时间为O(n),这样效率就会呈现线性下降,即使将 FD_SETSIZE 改大其性能也会很差。
3) 内核/用户空间内存拷贝问题, select 采取了内存拷贝方法让内核把 FD 消息通知给用户空间。
4)事件集,select的参数类型fd_set没有将文件描述符和事件绑定,它仅仅是一个文件描述符的集合,因此select需要提供3个“值-结果”类型的参数分别传入和输出可读、可写和异常等事件(调用该函数时,指定所关心的描述符的值,函数返回时,结果将指示哪些描述符已经就绪),也就是select函数会修改指针readset、writeset、exceptset所指向的描述符集。
一方面,使得select不能处理更多类型的事件,所能处理的事件类型只有读写异常三类;
另一方面,描述符集内任何与未就绪描述符对应的位返回时都会被清空,因此每次重新调用select时,都需要再次把所有的描述符集内所关心的位置为1。
5)select函数的定时是有函数的最后一个参数决定的,它是一个timeval结构体,用于指定这段时间的秒数和微秒数。
//timeval结构:
struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
3 poll模型
1)最大并发数限制,poll的第二个参数nfds是第一个参数指示的结构数据的元素个数,这个nfds并没有select的限制,它只受限于系统的内存空间(可以达到系统所允许打开的最大描述符的个数,即65535)。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
2)
效率问题,效率和select类似。
3)内核/用户空间内存拷贝问题,和select类似。
4)事件集,poll比select要“聪明”,它将描述符和事件定义在一起,任何事件都被统一处理,编程接口简洁许多。
一方面,poll可以监听的事件类型就可以更细分为很多种(参考文章:poll监听的事件类型)。
另一方面,而且内核每次修改的是pollfd结构体的revents成员,而events成员不变,因此下次重新调用poll无需重置pollfd类型中的事件集参数(避免了类似于select使用的的“值-结果”参数)。
5)poll的定时也是由函数的最后一个参数给出,但是它是一个int类型(指定函数要等待的毫秒数),而不是timeval结构体。
此外,从当今的可移植性角度考虑,支持select的系统比支持poll的系统要多。
epoll的突破
优点
epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪