开发高性能网络程序时。windows开发人员们言必称iocp,linux开发人员们则言必称epoll。大家都明确epoll是一种IO多路复用技术,能够很高效的处理数以百万计的socket句柄,比起曾经的select和poll效率高大发了。

我们用起epoll来都感觉挺爽,确实快,那么。它究竟为什么能够快速处理这么多并发连接呢?

先简单回想下怎样使用C库封装的3个epoll系统调用吧。

  1. int epoll_create(int size);
  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  3. int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

使用起来非常清晰,首先要调用epoll_create建立一个epoll对象。參数size是内核保证可以正确处理的最大句柄数。多于这个最大数时内核可不保证效果。

epoll_ctl能够操作上面建立的epoll,比如,将刚建立的socket增加到epoll中让其监控。或者把 epoll正在监控的某个socket句柄移出epoll。不再监控它等等。

epoll_wait在调用时,在给定的timeout时间内,当在监控的全部句柄中有事件发生时,就返回用户态的进程。

从上面的调用方式就能够看到epoll比select/poll的优越之处:由于后者每次调用时都要传递你所要监控的全部socket给select/poll系统调用,这意味着须要将用户态的socket列表copy到内核态,假设以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,很低效。

而我们调用epoll_wait时就相当于以往调用select/poll,可是这时却不用传递socket句柄给内核,由于内核已经在epoll_ctl中拿到了要监控的句柄列表。

所以。实际上在你调用epoll_create后,内核就已经在内核态開始准备帮你存储要监控的句柄了。每次调用epoll_ctl仅仅是在往内核的数据结构里塞入新的socket句柄。

在内核里,一切皆文件。所以,epoll向内核注冊了一个文件系统,用于存储上述的被监控socket。

当你调用epoll_create时,就会在这个虚拟的epoll文件系统里创建一个file结点。当然这个file不是普通文件,它仅仅服务于epoll。

epoll在被内核初始化时(操作系统启动)。同一时候会开辟出epoll自己的内核快速cache区,用于安置每个我们想监控的socket,这些socket会以红黑树的形式保存在内核cache里。以支持快速的查找、插入、删除。

这个内核快速cache区。就是建立连续的物理内存页。然后在之上建立slab层,简单的说。就是物理上分配好你想要的size的内存对象,每次使用时都是使用空暇的已分配好的对象。

  1. static int __init eventpoll_init(void)
  2. {
  3. ... ...
  4. /* Allocates slab cache used to allocate "struct epitem" items */
  5. epi_cache = kmem_cache_create("eventpoll_epi", sizeof(struct epitem),
  6. 0, SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC,
  7. NULL, NULL);
  8. /* Allocates slab cache used to allocate "struct eppoll_entry" */
  9. pwq_cache = kmem_cache_create("eventpoll_pwq",
  10. sizeof(struct eppoll_entry), 0,
  11. EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL);
  12. ... ...

epoll的高效就在于,当我们调用epoll_ctl往里塞入百万个句柄时,epoll_wait仍然能够飞快的返回,并有效的将发生事件的句柄给我们用户。

这是因为我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,只观察这个list链表里有没有数据就可以。有数据就返回。没有数据就sleep,等到timeout时间到后即使链表没数据也返回。

所以,epoll_wait很高效。

并且,通常情况下即使我们要监控百万计的句柄。大多一次也仅仅返回非常少量的准备就绪句柄而已,所以,epoll_wait仅须要从内核态copy少量的句柄到用户态而已,怎样能不高效?!

那么,这个准备就绪list链表是怎么维护的呢?当我们运行epoll_ctl时,除了把socket放到epoll文件系统里file对象相应的红黑树上之外,还会给内核中断处理程序注冊一个回调函数,告诉内核。假设这个句柄的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。

如此。一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们攻克了大并发下的socket处理问题。运行epoll_create时,创建了红黑树和就绪链表,运行epoll_ctl时,假设添加socket句柄,则检查在红黑树中是否存在,存在马上返回。不存在则加入到树干上,然后向内核注冊回调函数,用于其中断事件来暂时向准备就绪链表中插入数据。运行epoll_wait时立马返回准备就绪链表里的数据就可以。

最后看看epoll独有的两种模式LT和ET。不管是LT和ET模式。都适用于以上所说的流程。

差别是,LT模式下,仅仅要一个句柄上的事件一次没有处理完。会在以后调用epoll_wait时次次返回这个句柄,而ET模式仅在第一次返回。

这件事怎么做到的呢?当一个socket句柄上有事件时。内核会把该句柄插入上面所说的准备就绪list链表。这时我们调用epoll_wait。会把准备就绪的socket复制到用户态内存。然后清空准备就绪list链表,最后。epoll_wait干了件事。就是检查这些socket,假设不是ET模式(就是LT模式的句柄了),而且这些socket上确实有未处理的事件时,又把该句柄放回到刚刚清空的准备就绪链表了。所以,非ET的句柄,仅仅要它上面还有事件。epoll_wait每次都会返回。而ET模式的句柄。除非有新中断到,即使socket上的事件没有处理完,也是不会次次从epoll_wait返回的。

linux下的epoll怎样高效处理百万连接的更多相关文章

  1. linux下编程epoll实现将GPS定位信息上报到服务器

    操作系统:CentOS 开发板:fl2440 开发模块:A7(GPS/GPRS),RT3070(无线网卡) ********************************************** ...

  2. 比较一下Linux下的Epoll模型和select模型的区别

    一. select 模型(apache的常用) 1. 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Sel ...

  3. Linux下巧用my.cnf,mysql连接服务器不需要输入账号密码信息

    Linux下每次用mysql连接连接服务器,常常用如下方式: [root@localhost ~]# mysql -hlocalhost -uroot -p11111 每次都输入用户名,密码,多折腾人 ...

  4. 简单通讯聊天 群聊功能 Windows下的客户端 Linux下的epoll服务器

    1 服务器代码  Linux eclipse C++ //======================================================================= ...

  5. mongodb 之linux下安装、启动、停止、连接

    今天在linux上面安装了mongodb 1.下载linux的mongodb 2.在目录usr/local下创建文件夹mongodb,把安装包解压到该文件夹中 # mkdir mongodb # ta ...

  6. centos linux下配置固定ip,方便xshell连接

    如何给centos linux设置固定ip地址,设置Linux系统的固定IP地址 首先wmware打开虚拟机 打开xshell6连接虚拟机(比较方便,这里默认设置过Linux的ip,只是不固定,每次打 ...

  7. Linux下基于Erlang的高并发TCP连接压力实验

    1.实验环境: 联想小型机: 操作系统:RedHat Enterprise LinuxServer release6.4(Santiago) 内核版本号:Linux server1 2.6.32-35 ...

  8. Linux 下操作Mysql指令的总结 远程连接的设置

    参考博客:https://www.cnblogs.com/liaocheng/p/4243579.html (常用命令) https://www.cnblogs.com/zhangzhu/archiv ...

  9. 【Linux下Hadoop-eclipse-plus-3.2.0】编译Hadoop连接eclipse的插件遇见的一系列错误,崩溃的操作

    2019-09-02 23:35:22 前言:首先,我想吐槽下自己,居然花费了4到5个夜晚和中午的时间来做这件事情,直到刚才才顺利解决,我也挺佩服自己的! 我在这个过程中参考其他人的博客,非常感谢他们 ...

随机推荐

  1. Slow HTTP Denial of Service Attack 漏洞解决办法

    编辑 删除 问题名称: Slow HTTP Denial of Service Attack 问题URL http://10.238.*.*:58*** 风险等级: 高 问题类型: 服务器配置类 漏洞 ...

  2. Sqoop hive 和mysql 交互 完整案例

    本文完成:在hive里建管理表:注入部分数据:利用sqoop导入mysql中 期间:解决中文乱码问题   飞行报告故障表 建表命令 查看表 人工灌入少量数据 Windows系统向Linux系统数据传输 ...

  3. Java 基础入门随笔(6) JavaSE版——数组操作

    1.数组 概念:同一种类型数据的集合.其实就是数组就是一个容器. 好处:可以自动给数组中的元素从0开始编号,方便操作这些元素. 格式: ①. 元素类型[] 数组名 = new 元素类型[元素个数或数组 ...

  4. Codeforces_768_B_(二分)

    B. Code For 1 time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  5. Objective-C中copy 、retain以及ARC中新加入的strong、weak关键字的含义

    copy: 创建一个引用计数为1的对象,然后释放旧的对象 retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的引用计数为 1 Copy其实是建立了一个相同的对象,而retain不是: ...

  6. 安卓app测试之启动时间和电量监控

    一.启动时间监控 通过adb命令获取 adb shell am start -W /<packageName> /<activityName> 案例: adb shell am ...

  7. java学习_5_23

    Collection接口中定义的方法如下,所有继承自Collection接口的接口(List,Set)的实现类均实现了这些方法. List容器是有序.可重复的,常用的实现类:ArrayList,Lin ...

  8. Linux 安装 JDK 详解

    安装 JDK 说明:Linux 系统中安装软件需在 root 用户下进行. (1) 首先下载 jdk-8u131-linux-x64.rpm (2)将用户切换至 root,在 opt 文件夹下新建 s ...

  9. Windows 硬件开发人员怎样选择代码签名证书类型

    在建立 Windows 开发人员中心硬件仪表板帐户之前,你需要获取代码签名证书以保护数字信息的安全.此证书是用于建立你的公司对你所提交代码的所有权的接受标准.它让你可以用数字形式签署 PE 二进制文件 ...

  10. Navigator的学习

      Navigator 对象包含有关浏览器的信息.注释:没有应用于 navigator 对象的公开标准,不过所有浏览器都支持该对象.   我感觉需要看什么属性和方法,直接输出这个navigator,然 ...