开发高性能网络程序时。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. javascript按钮点击事件问题

    今天弄了个小测试,结果发现了点问题. 就是有一个按钮的点击事件,页面加载时候没反应,只有F12启用开发人员工具的时候才有反应.后来反复测试发现名字起的不太合理 function onclick(){ ...

  2. x86汇编之十(使用字符串)

    x86汇编之十(使用字符串) 转自网络,出处不详 一.传送字符串 Intel提供了完整的字符串传送指令,就像是MOV指令一样. 1.MOVS指令 1)movs指令格式 把字符串从一个位内存位置传送到另 ...

  3. PHP几个常用的概率算法

    算法一 /** * 全概率计算 * * @param array $p array('a'=>0.5,'b'=>0.2,'c'=>0.4) * @return string 返回上面 ...

  4. Python游戏开发:pygame游戏开发常用数据结构

    一.数组与列表 数组可以理解为简化的列表.像我们之前使用的pygame.sprite.Group这样的精灵组,也是一个列表.列表的元素是可变的,它具有添加.删除.搜索.排序等多种方法. 1.一维列表 ...

  5. tee命令用法

    用途说明 在执行Linux命令时,我们可以把输出重定向到文件中,比如 ls >a.txt,这时我们就不能看到输出了,如果我们既想把输出保存到文件中,又想在屏幕上看到输出内容,就可以使用tee命令 ...

  6. CAD使用SetxDataDouble写数据(网页版)

    主要用到函数说明: MxDrawEntity::SetxDataDouble 写一个Double扩展数据,详细说明如下: 参数 说明 [in] BSTR val 字符串值 szAppName 扩展数据 ...

  7. 梦想CAD控件网页版标注样式

    增加标注样式 _DMxDrawX::AddDimStyle 增加一个新的标注样式,如果当前已经有指定名的标注样式,就直接失败返回.详细说明如下: 参数 说明 BSTR pszName 新增加的标注样式 ...

  8. java计算两地距离(公里)

    //目标经度,目标纬度,自己经度,自己纬度 public static double getDistance(double lon1, double lat1, double lon2, double ...

  9. JSP页面中的指令标识

    JSP页面中的指令标识 制作人:全心全意 指令标识主要用于设定整个JSP页面范围内都有效的相关信息,它是被服务器解释并执行的,不会产生任何内容输出到网页中.也就是说,指令标识对于客户端浏览器是不可见的 ...

  10. UVALive 6510 Stickers

    Stickers Time Limit: 3000ms Memory Limit: 131072KB This problem will be judged on UVALive. Original ...