linux事件管理机制epoll

epoll是目前 Linux操作系统上最强大的事件管理机制,学会使用 epoll可以大幅提高机器的网络交互能力。下面我们来介绍一下 epoll以及它的实现原理。

假设有这样一个需求场景:有10万+的客户端与一个进程保持着 TCP连接,而每一时刻只有几十个或几百个TCP连接是活跃的,与服务器有 TCP包的交互,在每一时刻,进程只需要处理这100万连接中的一小部分连接。如何才能高效地处理这种场景呢?

有一种做法是这样的,进程想要获取客户端状态的时候,首先让操作系统收集那些 TCP连接上有事件发生,这个时候肯定要告诉操作系统要检查的 10万+个连接;然后操作系统找出这些连接中有事件发生的几百个连接,然后告诉进程,由进程针对这些有数据的连接进行进一步的处理。实际上,这就是 select或者 poll事件驱动方式的做法。

这样的做法就会有个非常明显的缺陷,在进程收集有事件的连接时,其实需要检查的大部分连接中是没有事件发生的。而且在每次收集事件时,都把这10万+连接的传给操作系统(这首先就是用户态内存到内核态内存的大量复制),然后操作系统遍历进程发送过来的连接一个个的进行遍历,查看那些连接上有事件发生,那些没有。这些复制传递、遍历会是巨大的资源浪费,当然也会有性能上的影响。
在 Linux内核 2.6版本之后,有了 epoll,它的做法就不是这样了。

epoll在 Linux内核中申请了一个简易的文件系统。在进程中使用 epoll的时候,首先调用 epoll_create创建 epoll句柄,当需要对 TCP连接进行监控时,直接调用 epoll_ctl向 epoll句柄中添加这10万+个连接的套接字即可。然后调用 epoll_wait收集发生事件的连接。

这样,只需要在进程启动时建立1个 epoll句柄,并在需要的时候向它添加或删除连接就可以了,然后选择阻塞或者非阻塞的方式调用 epoll_wait,操作系统就会返回发生事件的连接。

说起来很简单,Linux内核将如何实现以上的想法呢?当某一个进程调用 epoll_create方法时,Linux内核会创建一个 eventpoll结构体,这个结构体中有两个成员与 epoll的使用方式密切相关,如下所示:
在这里插入图片描述

每一个 epoll对象都有一个独立的 eventpoll结构体,这个结构体会在内核空间中创造独立的内存,用于存储使用 epoll_ctl方法向 epoll对象中添加进来的事件。这些事件都会挂到 rbr红黑树中,这样,重复添加的事件就可以通过红黑树而高效地识别出来(epoll_ctl方法会很快)。

所有添加到 epoll中的事件都会与设备(如网卡)驱动程序建立回调关系,相应的事件发生时会调用这里的回调方法。这个回调方法在内核中叫做 ep_poll_callback,它会把这样的事件放到上面的rdllist双向链表中(从而用户调用 epoll_wait的时候直接检查 rdlink就可以了)。(Nginx代码与 Linux内核代码很相似,红黑树与双向链表基本一致)。在 epoll中,对于每一个事件都会建立一个 epitem结构体,如下所示:

epoll高效的原因:

当调用 epoll_wait检查是否有发生事件的连接时,只是检查 eventpoll对象中的 rdllist双向链表是否有 epitem元素而已,如果 rdllist链表不为空,则把这里的事件复制到用户态内存中,同时将事件数量返回给用户。因此,epoll_wait的效率非常高。epoll_ctl在向 epoll对象中添加、修改、删除事件时,从 rbr红黑树中查找事件也非常快,也就是说,epoll是非常高效的,它可以轻易地处理百万级别的并发连接。

epoll高效的本质:

1.减少用户态和内核态之间的文件句柄拷贝;

2.减少对可读可写文件句柄的遍历。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值