`
ld_hust
  • 浏览: 171605 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

[转]SELECT、POOL、EPOLL的特性对比

阅读更多

select()系统调用提供一个机制来实现同步多元I/O:

 


#
include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select (int n,
fd_set 
*readfds,
fd_set 
*writefds,
fd_set 
*exceptfds,
struct timeval *timeout);

FD_CLR
(int fd, fd_set *set);
FD_ISSET
(int fd, fd_set *set);
FD_SET
(int fd, fd_set *set);
FD_ZERO
(fd_set *set);


调用select()将阻塞,直到指定的文件描述符准备好执行I/O,或者可选参数timeout指定的时间已经过去。
监视的文件描述符分为三类set,每一种对应等待不同的事件。readfds中列出的文件描述符被监视是否有数据可供读取(如果读取操作完成则不会阻塞)。writefds中列出的文件描述符则被监视是否写入操作完成而不阻塞。最后,exceptfds中列出的文件描述符则被监视是否发生异常,或者无法控制的数据是否可用(这些状态仅仅应用于套接字)。这三类set可以是NULL,这种情况下select()不监视这一类事件。
select()成功返回时,每组set都被修改以使它只包含准备好I/O的文件描述符。例如,假设有两个文件描述符,值分别是7和9,被放在readfds中。当select()返回时,如果7仍然在set中,则这个文件描述符已经准备好被读取而不会阻塞。如果9已经不在set中,则读取它将可能会阻塞(我说可能是因为数据可能正好在select返回后就可用,这种情况下,下一次调用select()将返回文件描述符准备好读取)。
第一个参数n,等于所有set中最大的那个文件描述符的值加1。因此,select()的调用者负责检查哪个文件描述符拥有最大值,并且把这个值加1再传递给第一个参数。
timeout参数是一个指向timeval结构体的指针,timeval定义如下:
#include <sys/time.h>
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* 10E-6 second */
};

如果这个参数不是NULL,则即使没有文件描述符准备好I/O,select()也会在经过tv_sec秒和tv_usec微秒后返回。当select()返回时,timeout参数的状态在不同的系统中是未定义的,因此每次调用select()之前必须重新初始化timeout和文件描述符set。实际上,当前版本的Linux会自动修改timeout参数,设置它的值为剩余时间。因此,如果timeout被设置为5秒,然后在文件描述符准备好之前经过了3秒,则这一次调用select()返回时tv_sec将变为2。
如果timeout中的两个值都设置为0,则调用select()将立即返回,报告调用时所有未决的事件,但不等待任何随后的事件。
文件描述符set不会直接操作,一般使用几个助手宏来管理。这允许Unix系统以自己喜欢的方式来实现文件描述符set。但大多数系统都简单地实现set为位数组。FD_ZERO移除指定set中的所有文件描述符。每一次调用select()之前都应该先调用它。
fd_set writefds;
FD_ZERO(&writefds);

FD_SET添加一个文件描述符到指定的set中,FD_CLR则从指定的set中移除一个文件描述符:
FD_SET(fd, &writefds); /* add 'fd' to the set */
FD_CLR(fd, &writefds); /* oops, remove 'fd' from the set */

设计良好的代码应该永远不使用FD_CLR,而且实际情况中它也确实很少被使用。
FD_ISSET测试一个文件描述符是否指定set的一部分。如果文件描述符在set中则返回一个非0整数,不在则返回0。FD_ISSET在调用select()返回之后使用,测试指定的文件描述符是否准备好相关动作:
if (FD_ISSET(fd, &readfds))
/* 'fd' is readable without blocking! */

因为文件描述符set是静态创建的,它们对文件描述符的最大数目强加了一个限制,能够放进set中的最大文件描述符的值由FD_SETSIZE指定。在Linux中,这个值是1024。本章后面我们还将看到这个限制的衍生物。
返回值和错误代码
select()成功时返回准备好I/O的文件描述符数目,包括所有三个set。如果提供了timeout,返回值可能是0;错误时返回-1,并且设置errno为下面几个值之一:
EBADF
给某个set提供了无效文件描述符。
EINTR
等待时捕获到信号,可以重新发起调用。
EINVAL
参数n为负数,或者指定的timeout非法。
ENOMEM
不够可用内存来完成请求。
--------------------------------------------------------------------------------------------------------------

poll()系统调用是System V的多元I/O解决方案。它解决了select()的几个不足,尽管select()仍然经常使用(多数还是出于习惯,或者打着可移植的名义):

#include <sys/poll.h>
int poll (struct pollfd *fds, unsigned int nfds, int timeout);

和select()不一样,poll()没有使用低效的三个基于位的文件描述符set,而是采用了一个单独的结构体pollfd数组,由fds指针指向这个组。pollfd结构体定义如下:

#include <sys/poll.h>

struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};

每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码。内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。合法的事件如下:
POLLIN
有数据可读。
POLLRDNORM
有普通数据可读。
POLLRDBAND
有优先数据可读。
POLLPRI
有紧迫数据可读。
POLLOUT
写数据不会导致阻塞。
POLLWRNORM
写普通数据不会导致阻塞。
POLLWRBAND
写优先数据不会导致阻塞。
POLLMSG
SIGPOLL消息可用。

此外,revents域中还可能返回下列事件:
POLLER
指定的文件描述符发生错误。
POLLHUP
指定的文件描述符挂起事件。
POLLNVAL
指定的文件描述符非法。

这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。使用poll()和select()不一样,你不需要显式地请求异常情况报告。
POLLIN | POLLPRI等价于select()的读事件,POLLOUT | POLLWRBAND等价于select()的写事件。POLLIN等价于POLLRDNORM | POLLRDBAND,而POLLOUT则等价于POLLWRNORM。
例如,要同时监视一个文件描述符是否可读和可写,我们可以设置events为POLLIN | POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。
timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。
返回值和错误代码
成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:
EBADF
一个或多个结构体中指定的文件描述符无效。
EFAULT
fds指针指向的地址超出进程的地址空间。
EINTR
请求的事件之前产生一个信号,调用可以重新发起。
EINVAL
nfds参数超出PLIMIT_NOFILE值。
ENOMEM
可用内存不足,无法完成请求。
--------------------------------------------------------------------------------------------------------------
以上内容来自《OReilly.Linux.System.Programming - Talking.Directly.to.the.Kernel.and.C.Library.2007》
--------------------------------------------------------------------------------------------------------------

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网卡驱动架构。

 

 

先说Select: 
1.Socket数量限制:该模式可操作的Socket数由FD_SETSIZE决定,内核默认32*32=1024. 
2.操作限制:通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍. 

后说Poll: 
1.Socket数量几乎无限制:该模式下的Socket对应的fd列表由一个数组来保存,大小不限(默认4k). 
2.操作限制:同Select. 

再说:Epoll: 
1.Socket数量无限制:同Poll 
2.操作无限制:基于内核提供的反射模式,有活跃Socket时,内核访问该Socket的callback,不需要遍历轮询. 

总体来说: 
大部分情况下,反射的效率都比遍历来的高,但是! 
但是当所有Socket都活跃的时候,反射还会更高么?这时候所有的callback都被唤醒,会导致资源的竞争.既然都是要处理所有的Socket,那么遍历是最简单最有效的实现方式.

举例来说: 
对于IM服务器,服务器和服务器之间都是长链接,但数量不多,一般一台60\70个,比如采用ICE这种架构设计,但请求相当频繁和密集,这时候通过反射唤醒callback不一定比用select去遍历处理更好. 
对于web portal服务器,都是浏览器客户端发起的http短链接请求,数量很大,好一点的网站动辄每分钟上千个请求过来,同时服务器端还有更多的闲置等待超时的Socket,这时候没必要把全部的Socket都遍历处理,因为那些等待超时的请求是大多数的,这样用Epoll会更好.

分享到:
评论

相关推荐

    linux_threadpool.zip_epoll select _epoll thread_epoll编程_thread

    标题和描述提到了几个关键概念:线程池(threadpool)、epoll、select以及与线程相关的编程。这些技术都是Linux网络编程中的核心元素,下面将详细讨论它们。 1. **线程池(Thread Pool)**: 线程池是一种多线程...

    libevent for qt网络模块,直接替换qt的select模型,支持epoll,select,pool.使用非常简单,无需修改以前的代码结构

    最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main...

    Linux C epoll.zip

    3. `thread_pool.c`和`thread_pool.h`:线程池的实现及其头文件,可能包含队列的数据结构和线程管理函数。 4. `mysql_connection.c`和`mysql_connection.h`:与MySQL数据库交互的代码和头文件,包括连接、查询和关闭...

    Go-基于epoll协程池的golang网络库

    Epoll是Event Poll的缩写,它改进了传统的select和poll函数,提供了一种面向多路复用I/O的解决方案。Epoll的核心思想是使用“红黑树”数据结构来存储待监控的文件描述符,并通过水平触发或边缘触发两种模式来提高...

    epoll内核代码学习

    本文将深入解析epoll的内核实现,探讨其核心数据结构、关键操作以及与select的对比。 ### 第一部分:系统调用与内部实现函数 1. `epoll_create`:调用`sys_epoll_create`,创建一个epoll实例,返回一个epoll文件...

    Linux C+epool

    在Linux系统中,epoll是一种高级的I/O事件通知机制,它是I/O复用的一种实现,替代了早期的poll和select。epoll提供了基于文件描述符的事件通知,能够处理大量的并发连接,并且在高并发场景下表现出极高的效率。epoll...

    C++ Socket

    在Linux系统中,`epoll`提供了更强大的文件描述符监控机制,相比于传统的`select`和`poll`,它具有更好的性能和可扩展性。 首先,`epoll`的工作原理是通过`epoll_create`创建一个`epoll`实例,然后使用`epoll_ctl`...

    基于多线程的高性能服务器程序的设计.pdf

    epoll通过事件通知的方式来实现对大量文件描述符的高效管理,它避免了传统select/poll模型在处理大量文件描述符时的性能瓶颈。 Select机制是另一种早期的I/O多路复用技术。在使用select时,可以通过fd_set来监控多...

    ACE.rar_ACE

    6. **命名服务**:ACE_WFMO_Reactor提供了Windows下的Wait For Multiple Objects功能,而ACE_Reactor则实现了跨平台的事件多路复用机制,如Select、Poll和Epoll,这些都是实现分布式系统中服务发现和协调的关键。...

    多线程HTTP服务器源代码

    4. **I/O复用**:了解如何实现重叠I/O模型,例如使用select、poll或epoll等系统调用来处理多个套接字的事件。 5. **线程池**:可能采用了线程池来更有效地管理和调度线程,减少线程创建和销毁的开销。 6. **错误处理...

    linuxyoushuang.rar

    书中会详细介绍套接字(socket)API,讲解TCP/IP协议栈的工作原理,以及如何使用阻塞与非阻塞I/O、多路复用(select、poll、epoll)来提高网络服务器的并发能力。 4. **并发与并行处理**:服务器程序通常需要处理...

    网络相关软件工程师入职前学习计划

    - Socket编程:练习使用socket进行编程,了解select和epoll等机制来提升性能。 - 协议分析:使用wireshark抓包工具,学习ARP、DHCP、HTTP等协议的交互过程。 - 网络报文处理:学习Netfilter框架和iptables的使用...

    ace技术内幕示例代码

    6. **I/O复用**:ACE通过`ACE_Select_Reactor`或`ACE_Proactor`实现了异步I/O,利用了如epoll或kqueue这样的系统调用来提高性能。 7. **对象适配器**:ACE的对象适配器如`ACE_Service_Object`和`ACE_Svc_Handler`,...

    设计_Linux多线程编程FAQ[参考].pdf

    为了提高I/O效率,可以使用非阻塞I/O(如select和epoll)来避免线程因等待I/O操作完成而阻塞。同时,文件描述符的管理,比如pending pool的使用,也是提高I/O性能的重要策略。 关于系统资源限制,ulimit命令用于...

    windows网络编程技术

    文件命名中,如008.pdf可能涉及网络编程模型,如阻塞I/O、非阻塞I/O、I/O复用(如select、poll、epoll)以及异步I/O(如IOCP)。011.pdf可能涵盖了套接字选项和控制消息,这些允许对套接字的行为进行微调,例如设置...

    cpp-nginx192源码通读分析注释

    Nginx支持多种事件模型,如epoll、kqueue、select等,以实现高效的I/O多路复用。这些模型在`src/core/ngx_event.c`中得以体现,通过`ngx_event_actions`结构体定义了各种操作。 3. **请求处理** 请求的处理流程...

    PHP-FPM不完全指南1

    12. `events.mechanism`:选择事件处理机制,如select、poll、epoll等。 13. `systemd_interval`:当PHP-FPM作为系统服务时,向服务器报告状态的频率。 进程池配置(Pool Definitions): 进程池允许你创建多个...

    ACE 5.5 参考手册

    3. **I/O 复用**:ACE 实现了多种I/O复用策略,如 select、poll 和 epoll 等,用于高效地处理大量并发连接。ACE_Win32_Acceptor 和 ACE_SOCK_Acceptor 类支持TCP连接,ACEUNIX_Acceptor 支持Unix域套接字。 4. **...

    Nginx源代码分析.pdf

    Nginx支持多种事件模型,如epoll、kqueue、select等,它们位于`event`目录下。这些事件模型允许Nginx在高并发环境下高效地处理连接和请求。 5. **HTTP服务器实现**: `http`目录下包含了Nginx作为HTTP服务器的...

Global site tag (gtag.js) - Google Analytics