epoll详细工作原理

2012-04-27 14:35 4007人阅读 评论(1) 收藏 举报
工作socketcachelistnullfile

开发高性能网络程序时,windows开发者们言必称iocp,linux开发者们则言必称epoll。大家都明白epoll是一种IO多路复用技术,可以非常高效的处理数以百万计的socket句柄,比起以前的select和poll效率高大发了。我们用起epoll来都感觉挺爽,确实快,那么,它到底为什么可以高速处理这么多并发连接呢?

先简单回顾下如何使用C库封装的3个epoll系统调用吧。

[cpp] view plaincopy
  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的内存对象,每次使用时都是使用空闲的已分配好的对象。

[cpp] view plaincopy
  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返回的。

epoll详细工作原理 http://blog.csdn.net/hdutigerkin/article/details/7517390相关推荐

  1. http://blog.csdn.net/hguisu/article/details/8836819

    1.  MySql+Memcached架构的问题 Memcached采用客户端-服务器的架构,客户端和服务器端的通讯使用自定义的协议标准,只要满足协议格式要求,客户端Library可以用任何语言实现. ...

  2. 嵌入式c程序易错点(转自http://blog.csdn.net/zhzht19861011/article/details/45508029)

    今天第一次在网上发表博客,虽然是转载的别人的文章,但还是有点小激动.转载的这篇文章里提到的问题很多都自己亲自踩过,有个别还踩过多次(感觉自己有点傻),也许是亲生经历的原因,看完后果断的转了,以后可以经 ...

  3. http://blog.csdn.net/luoshengyang/article/details/6651971

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6651971 在Android系统中,提供了独特 ...

  4. GitHu的诞生记 转 https://blog.csdn.net/fanpeihua123/article/details/58151161

    前言:这篇文章应读者要求所写,主要聊聊版本管理系统(Version Control System,简称 VCS), 这篇文章不能让你学会一门技术, 但是希望能帮你理解版本管理的原理. "人肉 ...

  5. http://blog.csdn.net/LANGXINLEN/article/details/50421988

    GitHub上史上最全的Android开源项目分类汇总 今天在看博客的时候,无意中发现了 @Trinea在GitHub上的一个项目 Android开源项目分类汇总, 由于类容太多了,我没有一个个完整地 ...

  6. https://blog.csdn.net/wutianxu123/article/details/82597337

    web安全/渗透测试--1--web安全原则 版权声明:本文为博主原创文章,转载本站文章请注明作者和出处,请勿用于任何商业用途. https://blog.csdn.net/wutianxu123/a ...

  7. git创建使用1https://blog.csdn.net/Hanani_Jia/article/details/77950594

    这篇文章是我自己写的关于GitHub的内容,从我刚听到这个直到设置成功每一步都有详细的步骤来解释,其中有一些截图或者代码来自于网上. 首先,我先对GitHub来一个简单的介绍,GitHub有一个很强大 ...

  8. http://blog.csdn.net/neiloid/article/details/7037093#

    1. 显示系统中全部Android平台: android list targets 2. 显示系统中全部AVD(模拟器): android list avd 3. 创建AVD(模拟器): androi ...

  9. GitHub 优秀的 Android 开源项目 转自http://blog.csdn.net/shulianghan/article/details/18046021

    原文地址为http://www.trinea.cn/android/android-open-source-projects-view/,作者Trinea 主要介绍那些不错个性化的View,包括Lis ...

最新文章

  1. HDU-1042 N! 大数乘法 (C语言)
  2. pandas(七) -- 数据分组
  3. Python数据分析之初识numpy常见方法使用案例
  4. python基础——Linux系统下的文件目录结构
  5. kendo grid输入框验证方法
  6. 【图说】Eclipse与Unity 3D协同工作
  7. Eigen教程(4)
  8. 微型计算机地基本结构,微型计算机地基本结构.ppt
  9. java如何实现成绩排序_java 成绩排序
  10. 12,三维Panel
  11. MybatisPlus手写sql分页
  12. kvm实战之搭建一个web服务来进行ks.cfg文件的引导(三)
  13. 动环监控系统的服务器配置,动环监控系统服务器维护
  14. victoriaMetrics无法获取抓取target的问题
  15. dw1000精度调节记录1
  16. 如何用python编写程序打开csv格式文件
  17. win+G没反应,Xbox game bar按不出来
  18. Cisco 第3章 基本网络连接和通信 测试考试答案
  19. 浅析云计算的六种架构
  20. 兼职币圈代言人?特斯拉CEO:加密货币是我的安全词

热门文章

  1. MOTOx4虽被人吐槽的外形,但这一点你肯定喜欢
  2. C#进行AutoCAD二次开发实例之倒角功能实现原理介绍
  3. 《金刚经》可以化解疠气
  4. js算法 -- 买股票
  5. C#自定义控件一步一步走
  6. 0到400之间的合数python_300到400以内的数是质数还是合数?
  7. app.publish_获取eZ Publish 5.x以便在Homestead上工作的13个步骤
  8. OpenJudge noi1805碎纸机
  9. (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  10. 小米也要造车了?为何手机厂商也爱造车?