导航:首页 > 编程语言 > javaepoll模型

javaepoll模型

发布时间:2023-03-31 15:40:42

1. Handler 中的 epoll

linux 中,epoll 机制是一个重要的机制。在 Android 中的 Handler,简单的利用了 epoll 机制,做到了消息队列的阻塞和唤醒。

epoll 机制相关的函数有

因为对于Handler 对烂带于 epoll 没有过于深入的使用,只是利用了 epoll 进行了阻塞和唤醒,还是比较好理解的。

于是,便利用 epoll 机制在mEpollFd上添加(EPOLL_CTL_ADD)了监听的 fd(mWakeEventFd);

java 层,next( )@Message 会阻塞到nativePollOnce(long ptr, int timeoutMillis),特别的是,当没有消息时,timeoutMillis = -1表示一直阻塞。如果有 delay 的消息,则 timeoutMillis 表示 delay的时间。

此时利用epoll 机制在 epoll_wait()上设置超时时间。当 timeoutMillis = -1时会一直等待知道有新消息来。否则当超时时间到达时信历贺,会返回到 next()@Message就可以处理那条 delay 的消息了。

当有新消息来临时并且是立刻执行的,enqueueMessage()@Message 会调用nativeWake(),否则会根据新来的消息的 delay 时滑派间和队列中的 delay 时间进行对比,消息队列是按照msg 的到达时间和 delay 时间进行排序,如果新来的消息在前并且需要 delay 也会进行 wake()

当往 mWakeEventFd 写入一个 1,便会从 epoll_wait() 马上返回。进行新一轮的消息处理。

另外,native 层的 Looper 的 epoll 机制没有这么简单,只是在 Handler 中只是简单地使用了。

Linux中的epoll

2. epoll 丢连接或丢包问题

问: 服务端用epoll 模型 , 客户端连接服务端发送大量udp 数据包, 经过一段时间后, 再次发送udp 数据包时,服务端收不到包,(或者说大量tcp连接服务端,服务端会丢失连接),为何?

答:1. 在服务端,用 ss -lu 查看 对应进程的recv-Q 是否占满

当 Recv-Q 可以理解为 对应进程socket 的接收缓存队列,系统维护,如果占满,系统会丢弃数据包。 可dmesg 查看相应系统日志。

epoll 模型有两种触发方式:基庆拆
一种叫好比拉尿,拉尿的时候都是一次拉完,只要膀胱中还有尿,就一直拉。这就是epoll 模型中的EPOLLLT (水平触发)模式,只要缓冲区里有数据,就一直触发差漏,(应用)赶快处理。
一种叫拉屎, 你完全可以拉到一半,然后提裤子走人,去接个电话。过一会儿,可能又有了拉屎的欲搏枣望, 再去拉屎。这就是epoll 模型中的EPOLLET(边沿触发)模式,当两个网络包同时到达时,只触发一次。

3. java Netty NIO 如何突破 65536 个端口的限制如何做到10万~50万的长连接

通常情况下是不可以突破的,端口有限制.单独对外提供请求的服务不用考虑端口数量问题,监听某一个端口即可.但是向提供代理服务器,就不得不考虑端口数量受限问题了.当前的1M并发连接测试,也需要在客户端突破6万可用端口的限制.端口为16进制,那么2的16次方值为65536,在linux系统里面,1024以下端口都是超级管理员用户(如root)才可以使用,普通用户只能使用大于1024的端口值.

服务器是只监听一个端口,所有的客户端连接,都是连接到服务器的同一个端口上的。也就是说服务器只是用了一个端口。就比如Http服务器。默认只用了80端口。

nio 在linux上使用的是epoll ,epoll支持在一个进程中打开的FD是操作系统最大文件句柄数,而不是你所说的16位short表示的文件句柄。 而 select模型 单进程打开的FD是受限的 select模型默认FD是1024 。操作系统最大文件句柄数跟内存有关,1GB内存的机器上,大概是10万个句柄左右。

4. 什么是 IO 模型

服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:
(1)同步阻塞IO(Blocking IO):即传统的IO模型。

(2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。

(3)IO多路复用(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。

(4)异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

5. java网络io模型有几种

#BIO---Blocking IO
- 每个socket一个线程,读写时线程处于阻塞状态。
优点:实现简单
缺点:无法满足高并发,高接入的需求

- 不使用线程池的BIO模型,除了无法满足高并发需求外,由于需要为每个请求创建一个线程,还可能因为接入大量不活跃连接而耗尽服务器资源。

- 使用线程池的BIO模型,虽然控制了线程数量,但由于其本质上读写仍是阻塞的,仍无法满足高并发需求。

#NIO---Non-Blocking IO(非阻塞IO)
##非阻塞IO和多路复用
非阻塞IO和多路复用实际上是两个不用的概念,由于两者通常结合在一起使用,因此两者往往被混为一谈。下面我将试着分清这两个概念:
###非阻塞IO
与BIO相对应,非阻塞IO的读写方法无论是否有数据都立即返回,因此可以通过轮询方式来实现,但轮询方式的效率并不比BIO有显著提高,因为每个连接仍然需要占用一个线程。下面是轮询方式实现的IO模式图:

###多路复用
- 多路复用结合非阻塞IO能够明显提高IO的效率,这也是Java1.4把非阻塞IO和多路复用同时发布的原因。
- 多路复用的核心是多路复用器(Selector),它是需要操作系统底层支持的,简单的说,就是进程把多个socket和它们关心的事件(比如连接请求或数据已准备好)都注册在多路复用器上,操作系统会在事件发生时通知多路复用器,这样进程就可以通过多路复用器知道在那个socket上发生了什么时间,从而进行对应的处理。
- 多路复用的优点在于只需要一个线程监测(阻塞或轮询方式均可)多路选择器的状态,只有在有事件需要发生时才会真正的创建线程进行处理,因此更适合高并发多接入的应用环境。

- 在Linux系统下,多路复用的底层实现是epoll方法,与select/poll的顺序扫描不同,epoll采用效率更高的事件驱动方式,而且epoll方式并没有socket个数限制。
##BIO和NIO的比较
- BIO适用于连接长期保持的应用,比如一个复杂系统中模块之间通过长连接来进行通信。
- NIO加多路复用的模式更适合短连接、高并发、多接入的情形,比如网络服务器。
##NIO网络编程的常用接口
##Reactor模式
Reactor模式用于解决事件分发处理的问题,Handler把自己的channel和关注的事件注册到Selector中,当对应的事件发生在自己的channel上时,对应的handler就会得到通知并进行处理。
- 单线程的Reactor
消息的分发、读写、处理都在一个线程中处理,是Reactor最简单的实现方式,如果消息的处理需要较长时间,会影响效率。

```java

//Reactor类,负责分发事件并调用对应的handler
class Reactor implements Runnable {

final Selector selector;

final ServerSocketChannel serverSocket;

//Reactor初始化

Reactor(int port) throws IOException {

selector = Selector.open();

serverSocket = ServerSocketChannel.open();

serverSocket.socket().bind(new InetSocketAddress(port));

serverSocket.configureBlocking(false); //必须配置为非阻塞

//Acceptor会在Reactor初始化时就注册到Selector中,用于接受connect请求
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);

sk.attach(new Acceptor()); //attach callback object, Acceptor

}

//分发消息并调用对应的handler
public void run() {
try {

while (!Thread.interrupted()) {

selector.select();

Set selected = selector.selectedKeys();

Iterator it = selected.iterator();

while (it.hasNext())

dispatch((SelectionKey)(it.next()); //Reactor负责dispatch收到的事件

selected.clear();

}

} catch (IOException ex) { /* ... */ }

}

void dispatch(SelectionKey k) {

Runnable r = (Runnable)(k.attachment()); //调用之前注册的callback对象

if (r != null)

r.run();

}

//Acceptor也是一个handler,负责创建socket并把新建的socket也注册到selector中

class Acceptor implements Runnable { // inner

public void run() {

try {

SocketChannel c = serverSocket.accept();

if (c != null)

new Handler(selector, c);

}

catch(IOException ex) { /* ... */ }

}

}

}

//Concrete Handler:用于收发和处理消息。
//在当前的实现中,使用Runnable接口作为每个具体Handler的统一接口
//如果在处理时需要参数和返回值,也可以为Handler另外声明一个统一接口来代替Runnable接口
final class Handler implements Runnable {

final SocketChannel socket;

final SelectionKey sk;

ByteBuffer input = ByteBuffer.allocate(MAXIN);

ByteBuffer output = ByteBuffer.allocate(MAXOUT);

static final int READING = 0, SENDING = 1;

int state = READING;

Handler(Selector sel, SocketChannel c) throws IOException {

socket = c; c.configureBlocking(false);

// Optionally try first read now

sk = socket.register(sel, 0);

sk.attach(this); //将Handler作为callback对象

sk.interestOps(SelectionKey.OP_READ); //第二步,接收Read事件

sel.wakeup();

}

boolean inputIsComplete() { /* ... */ }

boolean outputIsComplete() { /* ... */ }

void process() { /* ... */ }

public void run() {

try {

if (state == READING) read();

else if (state == SENDING) send();

} catch (IOException ex) { /* ... */ }

}

void read() throws IOException {

socket.read(input);

if (inputIsComplete()) {

process();

state = SENDING;

// Normally also do first write now

sk.interestOps(SelectionKey.OP_WRITE); //第三步,接收write事件

}

}

void send() throws IOException {

socket.write(output);

if (outputIsComplete()) sk.cancel(); //write完就结束了, 关闭select key

}

}

//上面 的实现用Handler来同时处理Read和Write事件, 所以里面出现状态判断

//我们可以用State-Object pattern来更优雅的实现

class Handler { // ...

public void run() { // initial state is reader

socket.read(input);

if (inputIsComplete()) {

process();

sk.attach(new Sender()); //状态迁移, Read后变成write, 用Sender作为新的callback对象

sk.interest(SelectionKey.OP_WRITE);

sk.selector().wakeup();

}

}

class Sender implements Runnable {

public void run(){ // ...

socket.write(output);

if (outputIsComplete()) sk.cancel();

}

}

}

```
- 多线程Reacotr
处理消息过程放在其他线程中执行

```java
class Handler implements Runnable {

// uses util.concurrent thread pool

static PooledExecutor pool = new PooledExecutor(...);

static final int PROCESSING = 3;

// ...

synchronized void read() { // ...

socket.read(input);

if (inputIsComplete()) {

state = PROCESSING;

pool.execute(new Processer()); //使用线程pool异步执行

}

}

synchronized void processAndHandOff() {

process();

state = SENDING; // or rebind attachment

sk.interest(SelectionKey.OP_WRITE); //process完,开始等待write事件

}

class Processer implements Runnable {

public void run() { processAndHandOff(); }

}

}

```
- 使用多个selector
mainReactor只负责处理accept并创建socket,多个subReactor负责处理读写请求

```java
Selector[] selectors; //subReactors集合, 一个selector代表一个subReactor

int next = 0;

class Acceptor { // ...

public synchronized void run() { ...

Socket connection = serverSocket.accept(); //主selector负责accept

if (connection != null)

new Handler(selectors[next], connection); //选个subReactor去负责接收到的connection

if (++next == selectors.length) next = 0;

}

}

```
#AIO
AIO是真正的异步IO,它于JDK1.7时引入,它和NIO的区别在于:
- NIO仍然需要一个线程阻塞在select方法上,AIO则不需要
- NIO得到数据准备好的消息以后,仍然需要自己把消息复制到用户空间,AIO则是通过操作系统的支持把数据异步复制到用户空间以后再给应用进程发出信号。

6. java目前流行的并发库有哪些

1、本地仔闷常用库java.util.concurrent
2、并发的宏戚睁关键点:线程、锁、模型
3、并发常用模型 actor、master-worker、epoll等,均可用本地库实现
4、第三方依赖库:akka、蔽岁netty、GCD等

7. IO模型及select,poll,epoll和kqueue的区别

(一)首先,介绍几种常见的I/O模型及其区别,如下:
blocking I/O
nonblocking I/O
I/O multiplexing (select and poll)
signal driven I/O (SIGIO)
asynchronous I/O (the POSIX aio_functions)—————异步IO模型最大的特点是 完成后发回通知。
阻塞与否,取决于实现IO交换的方式。
异步阻塞是基于select,select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄.
异步非阻塞直接在完成后通知,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。

1 blocking I/O
这个不用多解释吧,阻塞套接字。下图是它调用过程的图示:

重点解释下上图,下面例子都会讲到。首先application调用 recvfrom()转入kernel,注意kernel有2个过程,wait for data和 data from kernel to user。直到最后 complete后,recvfrom()才返回。此过程一直是阻塞的。

2 nonblocking I/O:
与blocking I/O对立的,非阻塞套接字,调用过程图如下:

可以看见,如果直接操作它,那就是个轮询。。直到内核缓冲区有数据。

3 I/O multiplexing (select and poll)
最常见的I/O复用模型,select。

select先阻塞,有活动套接字才返回。与blocking I/O相比,select会有两次系统调用,但是select能处理多个套接字。

4 signal driven I/O (SIGIO)
只有UNIX系统支持,感兴趣的课查阅相关资料

与I/O multiplexing (select and poll)相比,它的优势是,免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。

5 asynchronous I/O (the POSIX aio_functions)
很少有*nix系统支持,windows的IOCP则是此模型

完全异步的I/O复用机制,因为纵观上面其它四种模型,至少都会在由kernel data to appliction时阻塞。而该模型是当完成后才通知application,可见是纯异步的。好像只有windows的完成端口是这个模型,效率也很出色。
6 下面是以上五种模型的比较

可以看出,越往后,阻塞越少,理论上效率也是最优。
=====================分割线==================================
5种模型的比较比较清晰了,剩下的就是把select,epoll,iocp,kqueue按号入座那就OK了。
select和iocp分别对应第3种与第5种模型,那么epoll与kqueue呢?其实也于select属于同一种模型,只是更高级一些,可以看作有了第4种模型的某些特性,如callback机制。
为什么epoll,kqueue比select高级?
答案是,他们无轮询。因为他们用callback取代了。想想看,当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。
windows or *nix (IOCP or kqueue/epoll)?

诚然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系统,但是由于其系统本身的局限性,大型服务器还是在UNIX下。而且正如上面所述,kqueue/epoll 与 IOCP相比,就是多了一层从内核数据到应用层的阻塞,从而不能算作asynchronous I/O类。但是,这层小小的阻塞无足轻重,kqueue与epoll已经做得很优秀了。
提供一致的接口,IO Design Patterns
实际上,不管是哪种模型,都可以抽象一层出来,提供一致的接口,广为人知的有ACE,Libevent(基于reactor模式)这些,他们都是跨平台的,而且他们自动选择最优的I/O复用机制,用户只需调用接口即可。说到这里又得说说2个设计模式,Reactor and Proactor。见:Reactor模式--VS--Proactor模式。Libevent是Reactor模型,ACE提供Proactor模型。实际都是对各种I/O复用机制的封装。
Java nio包是什么I/O机制?
现在可以确定,目前的java本质是select()模型,可以检查/jre/bin/nio.dll得知。至于java服务器为什么效率还不错。。我也不得而知,可能是设计得比较好吧。。-_-。
=====================分割线==================================
总结一些重点:
只有IOCP是asynchronous I/O,其他机制或多或少都会有一点阻塞。
select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善
epoll, kqueue、select是Reacor模式,IOCP是Proactor模式。
java nio包是select模型。。
(二)epoll 与select的区别

1. 使用多进程或者多线程,但是这种方法会造成程序的复杂,而且对与进程与线程的创建维护也需要很多的开销。(Apache服务器是用的子进程的方式,优点可以隔离用户) (同步阻塞IO)

2.一种较好的方式为I/O多路转接(I/O multiplexing)(貌似也翻译多路复用),先构造一张有关描述符的列表(epoll中为队列),然后调用一个函数,直到这些描述符中的一个准备好时才返回,返回时告诉进程哪些I/O就绪。select和epoll这两个机制都是多路I/O机制的解决方案,select为POSIX标准中的,而epoll为Linux所特有的。

区别(epoll相对select优点)主要有三:
1.select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024 表示select最多同时监听1024个fd。而epoll没有,它的限制是最大的打开文件句柄数目。

2.epoll的最大好处是不会随着FD的数目增长而降低效率,在selec中采用轮询处理,其中的数据结构类似一个数组的数据结构,而epoll是维护一个队列,直接看队列是不是空就可以了。epoll只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数(把这个句柄加入队列),其他idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO。但是如果绝大部分的I/O都是“活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。

3.使用mmap加速内核与用户空间的消息传递。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。

关于epoll工作模式ET,LT

epoll有两种工作方式
ET:Edge Triggered,边缘触发。仅当状态发生变化时才会通知,epoll_wait返回。换句话,就是对于一个事件,只通知一次。且只支持非阻塞的socket。
LT:Level Triggered,电平触发(默认工作方式)。类似select/poll,只要还有没有处理的事件就会一直通知,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll.支持阻塞和不阻塞的socket。

三 Linux并发网络编程模型

1 Apache 模型,简称 PPC ( Process Per Connection ,):为每个连接分配一个进程。主机分配给每个连接的时间和空间上代价较大,并且随着连接的增多,大量进程间切换开销也增长了。很难应对大量的客户并发连接。
2 TPC 模型( Thread Per Connection ):每个连接一个线程。和PCC类似。
3 select 模型:I/O多路复用技术。
.1 每个连接对应一个描述。select模型受限于 FD_SETSIZE即进程最大打开的描述符数linux2.6.35为1024,实际上linux每个进程所能打开描数字的个数仅受限于内存大小,然而在设计select的系统调用时,却是参考FD_SETSIZE的值。可通过重新编译内核更改此值,但不能根治此问题,对于百万级的用户连接请求 即便增加相应 进程数, 仍显得杯水车薪呀。
.2select每次都会扫描一个文件描述符的集合,这个集合的大小是作为select第一个参数传入的值。但是每个进程所能打开文件描述符若是增加了 ,扫描的效率也将减小。
.3内核到用户空间,采用内存复制传递文件描述上发生的信息。
4 poll 模型:I/O多路复用技术。poll模型将不会受限于FD_SETSIZE,因为内核所扫描的文件 描述符集合的大小是由用户指定的,即poll的第二个参数。但仍有扫描效率和内存拷贝问题。
5 pselect模型:I/O多路复用技术。同select。
6 epoll模型:
.1)无文件描述字大小限制仅与内存大小相关
.2)epoll返回时已经明确的知道哪个socket fd发生了什么事件,不用像select那样再一个个比对。
.3)内核到用户空间采用共享内存方式,传递消息。
四 :FAQ
1、单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。 所以你有还是必要建立线程池来发挥更大的效能。
2、如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。
3、如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。
4、如果多个事件同时触发epoll,则多个事件会被联合在一起返回。
5、epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。
6、为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。

8. java nio和socket的select epoll有什么区别

当一个节点和多个节点建立连接时,如何高效的处理多个连接的数据,下面具体分析两者的区别。

  1. select函数

  2. 函数原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  3. 参数介绍:(1)nfds -- fdset集合中最大描述符值加1

  4. (2)fdset -- 一个位数组,其大小限制为_FD_SETSIZE(1024)

  5. 位数组的每一位代表的是其对应的描述符是否需要被检查。

  6. (3)readfds -- 读事件文件描述符数组

  7. (4 )writefds -- 写事件文件描述符数组

  8. (5)exceptfds -- 错误事件文件描述符数组

  9. (6)timeout -- 超时事件,该结构被内核修改,其值为超时剩余时间。

  10. 对应内核:select对应于内核中的sys_select调用,sys_select首先将第二三四个参数指向的fd_set拷贝到内核,然后对每个被SET的描 述符调用进行poll,并记录在临时结果中(fdset),如果有事件发生,select会将临时结果写到用户空间并返回;当轮询一遍后没有任何事件发生时,如果指定了超时时间,则select会睡眠到超时,睡眠结束后再进行一次轮询,并将临时结果写到用户空间,然后返

  11. 2. select/poll特点

  12. 传统的select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。

  13. poll的执行分三部分:

  14. (1).将用户传入的pollfd数组拷贝到内核空间,因为拷贝操作和数组长度相关,时间上这是一个O(n)操作

  15. (2).查询每个文件描述符对应设备的状态,如果该设备尚未就绪,则在该设备的等待队列中加入一项并继续查询下一设备的状态。 查询完所有设备后如果没有一个设备就绪,这时则需要挂起当前进程等待,直到设备就绪或者超时。设备就绪后进程被通知继续运行,这时再次遍历所有设备,以查找就绪设备。这一步因为两次遍历所有设备,时间复杂度也是O(n),这里面不包括等待时间

  16. (3). 将获得的数据传送到用户空间并执行释放内存和剥离等待队列等善后工作,向用户空间拷贝数据与剥离等待队列等操作的的时间复杂度同样是O(n)。

  17. 3. epoll机制

Linux 2.6内核完全支持epoll。epoll的IO效率不随FD数目增加而线性下降。

要使用epoll只需要这三个系统调用:epoll_create(2), epoll_ctl(2), epoll_wait(2)

epoll用到的所有函数都是在头文件sys/epoll.h中声明的,内核实现中epoll是根据每个fd上面的callback函数实现的。只有"活跃"的socket才会主动的去调用 callback函数,其他idle状态socket则不会。


如果所有的socket基本上都是活跃的---比如一个高速LAN环境,过多使用epoll,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。

3.1 所用到的函数:

(1)、int epoll_create(int size)

该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围

(2)、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。

如果调用成功返回0,不成功返回-1

int epoll_ctl{

int epfd,//由 epoll_create 生成的epoll专用的文件描述符

int op, //要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、

//EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 删除

int fd, //关联的文件描述符

struct epoll_event *event//指向epoll_event的指针

}

(3)、int epoll_wait(int

epfd, struct epoll_event *

events,int maxevents, int

timeout)

用于轮询I/O事件的发生,返回发生事件数

int epoll_wait{

int epfd,//由epoll_create 生成的epoll专用的文件描述符

struct epoll_event * events,//用于回传代处理事件的数组

int maxevents,//每次能处理的事件数

int timeout//等待I/O事件发生的超时值

//为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件

//为任意正整数的时候表示等这么长的时间,如果一直没有事件

//一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率

//如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率

}

epoll是为处理大批量句柄而作了改进的poll。

4. epoll的优点:


<1>支持一个进程打开大数目的socket描述符(FD)

select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显然太少了。这时候可以:

(1) 可以修改这个宏然后重新编译内核,不过资料也同时指出,这样也会带来网络效率的下降

(2) 可以选择多进程的解决方案,不过虽然linux上创建进程的代价比较下,但是仍旧是不可忽视的,所以也不是很完美的方案

epoll没有这样的限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,具体数组可以查看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网 卡驱动架构。

9. epoll为什么这么快epoll的实现原理是什么

以一个生活中的例子来解释.假设你在大学中读书,要等待一个朋友来访,而这个朋友只知道你在A号楼,但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面.如雀派早果你使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,...

10. I/O--多路复用的三种机制Select,Poll和Epoll对比

select、poll 和 epoll 都是 Linux API 提供的 IO 复用方式。

多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

我们先分析一下select函数

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

【参数说明】

int maxfdp1 指定待测试的文件描述字个数,它的值是待测试的最大描述字加1。

fd_set *readset , fd_set *writeset , fd_set *exceptset

fd_set可以理解为一个集合,这唤闹个集合中存放的是文件描述符(file descriptor),即文件句柄。中间的三个参数指定我们要让内核测试读、写和异常条件的文件描述符集合。如果对某一个的条件不感兴趣,就可以把它设为空指针。

const struct timeval *timeout timeout告知内核等待所指定文件描述符集合中的任何一个就绪可花多少时间。其timeval结构用于指定这段时世首间的秒数和微秒数。

【返回值】

int 若有就绪描述符返回其数目,若超时则为0,若出错则为-1

select()的机制中提供一种fd_set的数据结构,实际上是一个long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读。

从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。也就是说,poll只解决了上面的问题3,并没有解决问题1,2的性能开销问题。

下面是pll的函数原型:

poll改变了文件描述符集合的描述方式,使用了pollfd结构而不是select的fd_set结构,使得poll支持的文件描述符集合限制远大于select的1024

【参数说明】

struct pollfd *fds fds是一个struct pollfd类型的数组,用于存放需要检测其状态的socket描述符,并且调用poll函数之后fds数组不会被清空;一个pollfd结构体表示一个被监视的文件描述符,通过传递fds指示 poll() 监视多个文件描述符。其中,结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域,结构体的revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域

nfds_t nfds 记录数组fds中描述符的总数量

【返回值】

int 函数返回fds集合中就绪的读、写,或出错的描述符数量,和返罩返回0表示超时,返回-1表示出错;

epoll在Linux2.6内核正式提出,是基于事件驱动的I/O方式,相对于select来说,epoll没有描述符个数限制,使用一个文件描述符管理多个描述符,将用户关心的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的只需一次。

Linux中提供的epoll相关函数如下:

1. epoll_create 函数创建一个epoll句柄,参数size表明内核要监听的描述符数量。调用成功时返回一个epoll句柄描述符,失败时返回-1。

2. epoll_ctl 函数注册要监听的事件类型。四个参数解释如下:

epoll_event 结构体定义如下:

3. epoll_wait 函数等待事件的就绪,成功时返回就绪的事件数目,调用失败时返回 -1,等待超时返回 0。

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

LT和ET原本应该是用于脉冲信号的,可能用它来解释更加形象。Level和Edge指的就是触发点,Level为只要处于水平,那么就一直触发,而Edge则为上升沿和下降沿的时候触发。比如:0->1 就是Edge,1->1 就是Level。

ET模式很大程度上减少了epoll事件的触发次数,因此效率比LT模式下高。

一张图总结一下select,poll,epoll的区别:

epoll是Linux目前大规模网络并发程序开发的首选模型。在绝大多数情况下性能远超select和poll。目前流行的高性能web服务器Nginx正式依赖于epoll提供的高效网络套接字轮询服务。但是,在并发连接不高的情况下,多线程+阻塞I/O方式可能性能更好。

既然select,poll,epoll都是I/O多路复用的具体的实现,之所以现在同时存在,其实他们也是不同 历史 时期的产物

阅读全文

与javaepoll模型相关的资料

热点内容
古典武侠另类小说综合 浏览:111
有个叫艾米的是什么电影名字 浏览:597
linux硬件交互 浏览:208
用友软件考生文件夹在哪里 浏览:55
微信投票开发java 浏览:133
哪里可以看《永恒》泰国电影无删减 浏览:538
iphonefacetimewifi 浏览:632
桌面文件没有备份怎么恢复 浏览:207
主角在金三角贩卖军火的小说 浏览:587
疫情时期病例数据从哪里来 浏览:774
重生之红色军工铁血 浏览:86
男主姓皇甫领养女主 浏览:267
数控编程系统回参怎么操作 浏览:946
内地 战争影片3个字 浏览:107
国内最大的视频网站是什么 浏览:756
安徽联通刷钻代码 浏览:952
离我最近的电影院在哪 浏览:627
123.eecom 浏览:506
一部国外电影一个老头爱上一个老太 浏览:704
中云文化大数据秦龙 浏览:444

友情链接