高并发服务器开发与配置( 四 )


1.为什么是epoll , 而不是select?
(1)epoll支持在一个用户进程内打开最大系统限制的文件描述符select 最不能忍受的是一个进程所打开的FD是有一定限制的 , 由FD_SETSIZE设置 , 默认值是1024 。对于那些需要支持的上万连接数目的IM服务器来说显然太少了 。这时候一般有2种选择:一是可以选择修改这个宏然后重新编译内核 , 不过资料也同时指出这样会带来网络效率的下使用epoll进行高性能网络编程 降 , 二是可以选择多进程的解决方案(传统的Apache方案) , 不过虽然linux上面创建进程的代价比较小 , 但仍旧是不可忽视的 , 加上进程间数据同步远比不上线程间同步的高效 , 所以也不是一种完美的方案 。不过 epoll则没有这个限制 , 它所支持的FD上限是最大可以打开文件的数目 , 这个数字一般远大于2048,举个例子 , 在1GB内存的机器上大约是10万左右 , 具体数目可以cat /proc/sys/fs/file-max查看 , 一般来说这个数目和系统内存关系很大 。
(2)epoll的IO读取效率不随FD数目增加而线性下降传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合 , 不过由于网络延时 , 任一时间只有部分的socket是“活跃”的 , 但是select/poll每次调用都会线性扫描全部的集合 , 导致效率呈现线性下降 。但是epoll不存在这个问题 , 它只会对“活跃”的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的 。那么 , 只有“活跃”的socket才会主动的去调用 callback函数 , 其他idle状态socket则不会 , 在这点上 , epoll实现了一个“伪”AIO , 因为这时候推动力在os内核 。在一些 benchmark中 , 如果所有的socket基本上都是活跃的---比如一个高速LAN环境 , epoll并不比select/poll有什么效率 , 相反 , 如果过多使用epoll_ctl,效率相比还有稍微的下降 。但是一旦使用idle connections模拟WAN环境 , epoll的效率就远在select/poll之上了 。传统的select以及poll的效率会因为在线人数的线形递增而导致呈二次乃至三次方的下降 , 这些直接导致了网络服务器可以支持的人数有了个比较明显的限制 。select/poll线性扫描文件描述符 , epoll事件触发
(3)epoll使用mmap加速内核与用户空间的消息传递(文件描述符传递)这点实际上涉及到epoll的具体实现了 。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间 , 如何避免不必要的内存拷贝就很重要 , 在这点上 , epoll是通过内核与用户空间mmap同一块内存实现的 。
(4)epoll有2种工作方式epoll有2种工作方式:LT和ET 。LT(level triggered电平触发)是缺省的工作方式 , 并且同时支持block和no-block socket(阻塞和非阻塞).在这种做法中 , 内核告诉你一个文件描述符是否就绪了 , 然后你可以对这个就绪的fd进行IO操作 。如果你不作任何操作 , 内核还是会继续通知你的(如果你不作任何操作 , 会通知多次) , 所以 , 这种模式编程出错误可能性要小一点 。传统的select/poll都是这种模型的代表 。ET (edge-triggered边缘触发)是高速工作方式 , 只支持no-block socket 。在这种模式下 , 当描述符从未就绪变为就绪时 , 内核通过epoll告诉你 。然后它会假设你知道文件描述符已经就绪 , 并且不会再为那个文件描述符发送更多的就绪通知(无论如何 , 只通知一次) , 直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如 , 你在发送 , 接收或者接收请求 , 或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误) 。但是请注意 , 如果一直不对这个fd作IO操作(从而导致它再次变成未就绪) , 内核不会发送更多的通知(only once) , 不过在TCP协议中 , ET模式的加速效用仍需要更多的benchmark确认 。ET和LT的区别就在这里体现 , LT事件不会丢弃 , 而是只要读buffer里面有数据可以让用户读或写buffer为空 , 则不断的通知你 。而ET则只在事件发生之时通知 。可以简单理解为LT是水平触发 , 而ET则为边缘触发 。LT模式只要有事件未处理就会触发 , 而ET则只在高低电平变换时(即状态从1到0或者0到1)触发 。综上所述 , epoll适合管理百万级数量的文件描述符 。


推荐阅读