IO多路复用模型之epoll实现机制

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:

  • 当客户处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用。
  • 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
  • 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
  • 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
  • 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

 

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

 

目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

设想一下如下场景:有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻,通常只有几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况)。如何实现这样的高并发?

在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般只能处理几千的并发连接。

epoll的设计和实现与select完全不同。epoll通过在Linux内核中申请一个简易的文件系统(文件系统一般用什么数据结构实现?B+树)。把原先的select/poll调用分成了3个部分:

1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)

2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字

3)调用epoll_wait收集发生的事件的连接

如此一来,要实现上面说是的场景,只需要在进程启动时建立一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除连接。同时,epoll_wait的效率也非常高,因为调用epoll_wait时,并没有一股脑的向操作系统复制这100万个连接的句柄数据,内核也不需要去遍历全部的连接。

下面来看看Linux内核具体的epoll机制实现思路。

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。eventpoll结构体如下所示:

1

2

3

4

5

6

7

8

struct eventpoll{

    ....

    /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/

    struct rb_root  rbr;

    /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/

    struct list_head rdlist;

    ....

};

  

每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。

而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

在epoll中,对于每一个事件,都会建立一个epitem结构体,如下所示:

1

2

3

4

5

6

7

struct epitem{

    struct rb_node  rbn;//红黑树节点

    struct list_head    rdllink;//双向链表节点

    struct epoll_filefd  ffd;  //事件句柄信息

    struct eventpoll *ep;    //指向其所属的eventpoll对象

    struct epoll_event event; //期待发生的事件类型

}

  当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。

从上面的讲解可知:通过红黑树和双链表数据结构,并结合回调机制,造就了epoll的高效。

OK,讲解完了Epoll的机理,我们便能很容易掌握epoll的用法了。一句话描述就是:三步曲。

第一步:epoll_create()系统调用。此调用返回一个句柄,之后所有的使用都依靠这个句柄来标识。

第二步:epoll_ctl()系统调用。通过此调用向epoll对象中添加、删除、修改感兴趣的事件,返回0标识成功,返回-1表示失败。

第三步:epoll_wait()系统调用。通过此调用收集收集在epoll监控中已经发生的事件。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IO多路复用就是一种事件驱动的机制,它是指在单个线程通过监听多个流的状态来同时接收和发送多个请求,同时提高资源利用效率,减少不必要的阻塞和等待时间。其主要的实现方式有以下几种: 1、select模型(轮询) select模型是一种最基本的IO多路复用技术,它通过轮询来监听IO事件,并阻塞在select()调用上,等待任何一个流的状态改变,然后根据事件类型进行相应的读写操作。 2、poll模型(链表) poll模型和select模型类似,也是通过轮询来监听IO事件,但是其在实现时使用链表来管理IO流,可以有效提高效率,同时也可以处理更多的并发请求。 3、epoll模型(红黑树) epoll模型可以说是目前最为流行的IO多路复用技术,它通过使用红黑树来管理IO流,监听事件只需要在需要监听的文件描述符上设置回调函数,每当有IO事件发生时就会调用回调函数,处理读写请求时会将相关的数据结构从内核空间复制到用户空间,这样就大大提高了效率。 4、kqueue模型(八叉树) kqueue模型是在BSD UNIX系统首先实现的一种IO多路复用技术,其采用八叉树的数据结构来管理IO事件,可以高效处理大量并发请求,同时又不会增加过多的系统负担。 总之,无论采用哪种IO多路复用技术,都可以有效提高系统的处理效率和资源利用率,降低了系统的负担,从而使得面对大量并发请求时,系统可以更加稳定和高效地进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值