linux下epoll如何实现高效处理百万句柄的

开发高性能网络程序时,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.   
  5.     /* Allocates slab cache used to allocate "struct epitem" items */  
  6.     epi_cache = kmem_cache_create("eventpoll_epi"sizeof(struct epitem),  
  7.             0, SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC,  
  8.             NULL, NULL);  
  9.   
  10.     /* Allocates slab cache used to allocate "struct eppoll_entry" */  
  11.     pwq_cache = kmem_cache_create("eventpoll_pwq",  
  12.             sizeof(struct eppoll_entry), 0,  
  13.             EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL);  
  14.   
  15.  ... ...  


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返回的。

转载:https://blog.csdn.net/russell_tao/article/details/7160071

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463693.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

89个嵌入式相关概念,你懂了几个?

嵌入式、计算机的学习,最基础的就是弄清一些概念。嵌入式、计算机相关的概念很多,这次汇总整理一些:1. 操作系统(Operating System,OS):是管理计算机硬件与软件资源的系统软件,同时也…

每天一个linux命令(lsof)

losf----系统级监控、诊断工具安装:yum install lsof格式:lsof [选项] [绝对路径文件名 | 其他参数]主要选项:D:递归查找除该路径下所有打开的文件[rootroot ~]# lsof D /usr/lib COMMAND PID USER FD TYPE DEVICE SIZE…

正确使用PresentModalViewController

Present ViewController Modally 一、主要用途 弹出模态ViewController是IOS变成中很有用的一个技术,UIKit提供的一些专门用于模态显示的ViewController,如UIImagePickerController等。弹出模态ViewController主要使用于一下这几种情形: 1、…

word 2007 中插入图片无法显示,只能显示底部一部分

故障现象 向正在编辑的Word文档中插入一个图形时,发现插入的图形只显示出了一部分。下半部分,图片上面有文字,还能输入文字和回车。好像图片在文字下面一样,就露出了一个底部。无论怎样调整都不能解决问题,如果用浮…

使用VLC转码,在HTML5页面播放实时监控

首先要获取摄像机品牌的RTSP地址&#xff1a; 如大华的是rtsp://user:pwdip:port/cam/ realmonitor?channel1&subtype0 海康的是rtsp://user:pwdip:port/MPEG-4/ch1/main/av_stream Html5 <video>并不支持rtsp,所以使用vlc进行转码,将rtsp转http流,这样<video&g…

聊聊Linux中线程和进程的联系与区别!

大家好&#xff0c;推荐飞哥的一篇文章&#xff01;关于进程和线程&#xff0c;在 Linux 中是一对儿很核心的概念。但是进程和线程到底有啥联系&#xff0c;又有啥区别&#xff0c;很多人还都没有搞清楚。在网上对进程和线程的讨论中&#xff0c;很多都是聚集在这二位有啥不同。…

Docker swarm 实战-部署wordpress

Docker swarm 实战-部署wordpress 创建一个overlay的网络 docker network create -d overlay demo6imq8da3vcwvj2n499k4bwdlt docker network ls NETWORK ID NAME DRIVER SCOPE feea5ba8507b bridge bridge …

浅谈jQuery的选择器

jQuery的选择器可谓之强大无比&#xff0c;这里简单地总结一下常用的元素查找方法 $("A B") 查找A元素下面的所有子节点&#xff0c;包括非直接子节点 $("A>B") 查找A元素下面的直接子节点 $("AB") 查找A元素后面的兄弟节点&#xff0c;包括非…

注册广播

动态注册&#xff1a;detectionSDkBroadcastReceiver new DetectionSDkBroadcastReceiver();IntentFilter intentFilter new IntentFilter();intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);intentFilter.addDataS…

2018年上半年总结

2018年上半年即将过去&#xff0c;在这半年里经历了很多的事情&#xff0c;让自己在各方面成长了很多&#xff0c;但也失去了很多&#xff0c;有些事情让我刻骨铭心&#xff0c;也让我明白了很多的事情&#xff0c;包括&#xff1a;家庭、工作、生活、相处等。 先说下工作上的事…

比GDB方便n倍的调试工具——CGDB

CGDB 是GDB的前端&#xff0c;在终端窗口中意图形化的形式来调试代码(基于ncurse)&#xff0c;非常方便。相对于GDB来说&#xff0c;可以很大的提高效率。这篇文章就来分享一下CGDB的最基本使用方法&#xff0c;如果是第一次听说&#xff0c;强烈建议您体验一下&#xff0c;一定…

linux--切换ipython解释器到python3

Ipython修改为python3解释器&#xff1a; which ipython --得到路径 cat 路径--查看执行的解释器版本 sudo gedit 路径--修改解释器版本为python3保存即可&#xff0c;保存的时候提示异常&#xff0c;这个是正常现象&#xff0c;实际上已经保存成功。测试 which ipython 路径 c…

数据库中字段类型Number(n,m)大概说明

number可以存储浮点数&#xff0c;也可以存储整数。Number(n,m) int类型只能存放整数。 a、number(4,3)是表示这个数一共有4位是有效位,后面的3表示有3个是小数也就是这个数&#xff0c;只能是1.234,这样格式的最大只能是9.999,b、number&#xff08;3,4&#xff09; 表示这个…

WCF Security userName/Password

1. Transfer SecurityTransfer Security 主要包括三个方面&#xff1a; "消息完整性(Message Integrity)"、"消息机密性 (Message Confidentiality)" 和 "交互验证(Mutual Authentication)"。消息完整性必须确保消息在传输过程中没有被篡改&…

Sencha Touch2中数据的获取

根据Sencha Touch技术框架的特点&#xff0c;前台展示的绝大部分数据都是通过ajax方式获取&#xff0c;譬如列表数据的获取、表单数据的获取等等。列表数据的获取&#xff0c;一般是通过store组件和list组件进行结合&#xff1b;表单数据的获取通常使用Ext.request方式获取。列…

boost库中mutex、condition_variable与mutex::scoped_lock联合使用实现线程之间的通信

最近在公司负责一个线程池的模块&#xff0c;里面用到了boost库中的mutex、condition_variable与mutex::scoped_lock&#xff0c;在此总结下线程池在使用时的方式和要点&#xff0c;这里记录了线程之间的通信方式&#xff0c;希望对学习线程之间(同一个进程)通信的同志们有所帮…

caffe运行训练脚本时报错:Unknown bottom blob 'data' (layer 'conv1',bottom index 0)

报错的两种报错原因&#xff1a; 1.输入数的路径错误&#xff0c;需要将路径进行修改排查目录是否出错 2.训练原数据格式不对 3.train.prototxt文件中并未设置test层&#xff0c;而在solver层则设置了test的迭代等参数 两种解决方法 1.对错误原因1&#xff0c;则改为正确路径 2…

保护视力,我写的一个定时提醒的小玩意。

做程序员2年了&#xff0c;感觉视力越来越差。有时候常常工作到忘记休息。于是就想写一个能够定时提醒的小东西(公司不让从网络下载别的程序)。 功能: 1.能够每隔一段时间提醒我休息&#xff0c;做做眼保健操。 2.能够自己设定时间间隔. 运行环境&#xff1a;.ne…

Matrix Computations 1

matrix computation转载于:https://www.cnblogs.com/stoneresearch/archive/2012/06/05/4336290.html

Linux下修改只读文件

最近在linux Ubuntu下配置hadoop&#xff0c;遇到了一个只读文件core-site.xml&#xff0c;需要修改其中的内容&#xff0c;但是该文件是只读的。查了资料比较简单&#xff1a; chattr -i 文件 让只读文件可编辑 chattr i 文件 让文件只读 ubuntu下使用&#xff1a; sud…