(1)导言:
首先,我强烈建议大家阅读Richard Stevens著作《TCP/IP Illustracted Volume 1,2,3》和《UNIX
Network Programming Volume
1,2》。虽然他离开我们大家已5年多了,不过他的书依然是进入网络编程的最直接的道路。其中的3卷的《TCP/IP
Illustracted》卷1是必读-如果你不了解tcp协议各个选项的周详定义,你就失去了优化程式重要的一个手段。卷2,3能选读一下。比如卷2
讲解的是4.4BSD内核TCP/IP协议栈实现----这个版本的协议栈几乎影响了目前所有的主流os,不过因为年代久远,内容不一定那么vogue.
在这里我多推荐一本《The Linux Networking Architecture--Design and Implementation of
Network Protocols in the Linux Kernel》,以2.4内核讲解Linux
TCP/IP实现,相当不错.作为一个现实世界中的实现,非常多时候你必须作非常多权衡,这时候参考一个久经考验的系统更有实际意义。举个例子,linux内核中sk_buff结构为了追求速度和安全,牺牲了部分内存,所以在发送TCP包的时候,无论应用层数据多大,sk_buff最小也有272的字节.
其实对于socket应用层程式来说,《UNIX
Network Programming Volume 1》意义更大一点.2003年的时候,这本书出了最新的第3版本,不过主要还是修订第2版本。其中第6章《I/O
Multiplexing》是最重要的。Stevens给出了网络IO的基本模型。在这里最重要的莫过于select模型和Asynchronous
I/O模型.从理论上说,AIO似乎是最高效的,你的IO操作能即时返回,然后等待os告诉你IO操作完成。不过一直以来,怎么实现就没有一个完美的方案。最著名的windows完成端口实现的AIO,实际上也是内部用线程池实现的罢了,最后的结果是IO有个线程池,你应用也需要一个线程池......
非常多文件其实已指出了这带来的线程contexttch带来的代价。
在linux
平台上,关于网络AIO一直是改动最多的地方,2.4的年代就有非常多AIO内核patch,最著名的应该算是SGI那个。不过一直到2.6内核发布,网络模块的AIO一直没有进入稳定内核版本(大部分都是使用用户线程模拟方法,在使用了NPTL的linux上面其实和windows的完成端口基本上差不多了)。2.6内核所支持的AIO特指磁盘的AIO---支持io_submit(),io_getevents()及对Direct
IO的支持(就是绕过VFS系统buffer直接写硬盘,对于流服务器在内存平稳性上有相当帮助)。
所以,剩下的select模型基本上就是我们在linux上面的唯一选择,其实,如果加上no-block
socket的设置,能完成一个"伪"AIO的实现,只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受的缺点,所以改进一直是2.4-2.5研发版本内核的任务,包括/dev/poll,realtime
signal等等。最终,Davide
Libenzi研发的epoll进入2.6内核成为正式的解决方案
(2)epoll的好处
1>支持一个进程打开大数目的socket描述符(FD)
select
最不能忍受的是个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显然太少了。这时候你一是能选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是能选择多进程的解决方案(传统的
Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。不过
epoll则没有这个限制,他所支持的FD上限是最大能打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目能cat
/proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系非常大。
2>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之上了。
3>使用mmap加速内核和用户空间的消息传递。
这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,怎么避免不必要的内存拷贝就非常重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。而如果你想我相同从2.5内核就关注epoll的话,一定不会忘记手工
mmap这一步的。
4>内核微调
这一点其实不算epoll的好处了,而是整个linux平台的好处。也许你能怀疑linux平台,不过你无法回避linux平台赋予你微调内核的能力。比如,内核TCP/IP协议栈使用内存池管理sk_buff结构,那么能在运行时期动态调整这个内存pool(skb_head_pool)的大小---
通过echo
XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函数的第2个参数(TCP完成3次握手的数据包队列长度),也能根据你平台内存大小动态调整。更甚至在一个数据包面数目巨大但同时每个数据包本身大小却非常小的特别系统上尝试最新的NAPI网卡驱动架构。
(3)epoll的使用
令人高兴的是,2.6内核的epoll比其2.5研发版本的/dev/epoll简洁了许多,所以,大部分情况下,强大的东西往往是简单的。唯一有点麻烦是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确认。
epoll只有epoll_create,epoll_ctl,epoll_wait
3个系统调用,具体用法请参考http://www.xmailserver.org/linux-patches/nio-improve.html
,
在http://www.kegel.com/rn/也有一个完整的例子,大家一看就知道怎么使用了
(4)Leader/follower模式线程pool实现,及和epoll的配合
.....未完成,主要是要避免过多的epoll_ctl调用,及尝试使用EPOLLONESHOT加速......
(5)benchmark
.......未完成
分享到:
相关推荐
Epoll(Event Poll)是Linux 2.6内核引入的改进版的poll机制,它提供了基于文件描述符的事件多路复用技术。epoll相比旧的select和poll有更高的性能和可扩展性,尤其在处理大量并发连接时。epoll通过`epoll_create()`...
Linux 2.6内核对于嵌入式系统来说具有重要意义。它引入了可抢占内核的概念,使得高优先级的任务可以打断正在运行的低优先级任务,从而提高了系统的响应时间和用户交互性。虽然不是真正的实时操作系统(RTOS),但是...
5. **网络编程**:在Linux 2.6.22.6中,`socket()`函数创建网络套接字,`bind()`将套接字绑定到特定端口,`listen()`和`accept()`处理连接请求,`send()`和`recv()`发送和接收数据。此外,还有`select()`, `poll()`,...
##### 2.6 网络编程 - **套接字编程**:`socket()`函数用于创建套接字,是网络编程的基础。 - **TCP/IP协议栈**:详细介绍TCP/IP协议的工作原理,包括连接建立、数据传输、连接关闭等过程。 - **多路复用**:使用`...
相比之下,`epoll`是Linux 2.6内核引入的一种优化过的I/O多路复用机制,特别是针对大规模句柄的情况。`epoll`的主要优点包括: 1. **无限制的句柄数**:`epoll`不再受到`FD_SETSIZE`的限制,可以处理成千上万的并发...
Epoll 是 Linux 2.6 内核版本中引入的一个新特性,旨在解决 Select 和 Poll 存在的问题。Epoll 主要的优势在于: ##### 2.1 没有最大并发连接限制 Epoll 没有固定的最大并发连接数限制,而是受限于操作系统能打开...
epoll是Linux 2.6内核引入的一种优化的I/O复用机制,专门针对大规模连接设计。它克服了select和poll的性能瓶颈,即使在处理成千上万个连接时也能保持较高的效率。epoll的核心在于其边缘触发(Edge Triggered, ET)和...
为了解决这个问题,文章深入研究了线程池技术和Linux 2.6内核下的I/O多路复用技术,并在此基础上设计并实现了一种结合线程池和I/O多路复用的高性能网络服务器编程模型。 线程池是一种管理线程的技术,它预先创建一...
(epoll技术从linux内核2.6开始引入的)。 (2)epoll技术的性能,可以说非常惊艳,它是能够使单台计算机支撑数百万甚至数十万上百万并发的核心技术,远优于其他I/O模型或I/O函数(如select、poll函数),select和...
1. **Linux内核概述**:Linux内核是操作系统的核心,负责管理硬件资源,提供系统调用接口,以及进程管理、内存管理、文件系统、网络协议栈等基础服务。第三版会涵盖从2.6到4.x系列内核的主要更新和改进。 2. **进程...
Epoll是Linux 2.6内核版本引入的,它与早期的select和poll模型相比,在性能上有显著的提升。 在讨论Epoll的用法之前,首先要了解一些基础概念和现有技术的局限性。在Linux系统中,每个进程都有一个文件描述符(File...
**epoll** 是 Linux 内核 2.6 版本引入的一种高效的 I/O 多路复用技术,相较于传统的 **select** 和 **poll** 方法,epoll 提供了更高效、更灵活的事件通知机制,特别适用于高并发场景下的网络编程。 #### 二、...
内容简介《Linux高性能服务器编程》是Linux服务器编程领域的经典著作,由资深Linux软件开发工程师撰写,从网络协议、服务器编程核心要素、原理机制、工具框架等多角度全面阐释了编写高性能Linux服务器应用的方法、...
Epoll是Event Polling的缩写,它是Linux 2.6及以上版本内核提供的一种I/O事件通知机制。与select和poll相比,Epoll支持边缘触发(ET)和水平触发(LT)两种模式,且通过文件描述符(FD)管理,减少了上下文切换次数...
Epoll是Linux内核在2.6版本中引入的I/O多路复用模型,是对传统Select和Poll的改进。Epoll的核心思想是“事件驱动”,它通过一个Epoll句柄来管理一组文件描述符(FD),当这些FD就绪时,Epoll会通知用户进程。这种...
Linux 2.6内核引入的epoll提供了更高效的I/O多路复用机制。一个线程监听和接收连接,设置非阻塞模式并注册到epoll事件中,另一个线程处理数据发送。epoll使用边缘触发模式,能有效处理大规模并发连接,但编程复杂度...
- **epoll (Linux 2.6+)**:Linux内核中引入的改进型I/O多路复用接口,显著提高了性能。 - **Polyakov's kevent (Linux 2.6+)**:基于FreeBSD kqueue的实现,用于Linux系统。 - **Drepper's New Network ...
- **epoll** 是在Linux 2.6内核引入的,是对早期的`select`和`poll`函数的改进。epoll的优势在于没有描述符数量的限制,而且它采用“事件就绪通知”模型,提高了性能。 2. **epoll接口** - **epoll_create()**: ...