linux下epoll如何实现高效处理
linux下epoll如何实现高效处理
作者
digoal
日期
2016-11-10
标签
Linux , 内核 , epoll , 网络编程 , 高并发
背景
本文转自
http://www.cnblogs.com/debian/archive/2012/02/16/2354469.html
开发高性能网络程序时,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);
使用起来很清晰,
1 首先要调用epoll_create建立一个epoll对象。参数size是内核保证能够正确处理的最大句柄数,多于这个最大数时内核可不保证效果。
2 epoll_ctl可以操作上面建立的epoll,例如,将刚建立的socket加入到epoll中让其监控,或者把 epoll正在监控的某个socket句柄移出epoll,不再监控它等等。
3 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 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返回的。
1 /* 2 * Each file descriptor added to the eventpoll interface will 3 * have an entry of this type linked to the hash. 4 */ 5 struct epitem { 6 /* RB-Tree node used to link this structure to the eventpoll rb-tree */ 7 struct rb_node rbn; 8 //红黑树,用来保存eventpoll 9 10 /* List header used to link this structure to the eventpoll ready list */ 11 struct list_head rdllink; 12 //双向链表,用来保存已经完成的eventpoll 13 14 /* The file descriptor information this item refers to */ 15 struct epoll_filefd ffd; 16 //这个结构体对应的被监听的文件描述符信息 17 18 /* Number of active wait queue attached to poll operations */ 19 int nwait; 20 //poll操作中事件的个数 21 22 /* List containing poll wait queues */ 23 struct list_head pwqlist; 24 //双向链表,保存着被监视文件的等待队列,功能类似于select/poll中的poll_table 25 26 /* The "container" of this item */ 27 struct eventpoll *ep; 28 //指向eventpoll,多个epitem对应一个eventpoll 29 30 /* The structure that describe the interested events and the source fd */ 31 struct epoll_event event; 32 //记录发生的事件和对应的fd 33 34 /* 35 * Used to keep track of the usage count of the structure. This avoids 36 * that the structure will desappear from underneath our processing. 37 */ 38 atomic_t usecnt; 39 //引用计数 40 41 /* List header used to link this item to the "struct file" items list */ 42 struct list_head fllink; 43 双向链表,用来链接被监视的文件描述符对应的struct file。因为file里有f_ep_link,用来保存所有监视这个文件的epoll节点 44 45 /* List header used to link the item to the transfer list */ 46 struct list_head txlink; 47 双向链表,用来保存传输队列 48 49 /* 50 * This is used during the collection/transfer of events to userspace 51 * to pin items empty events set. 52 */ 53 unsigned int revents; 54 //文件描述符的状态,在收集和传输时用来锁住空的事件集合 55 }; 56 57 //该结构体用来保存与epoll节点关联的多个文件描述符,保存的方式是使用红黑树实现的hash表. 58 //至于为什么要保存,下文有详细解释。它与被监听的文件描述符一一对应. 59 struct eventpoll { 60 /* Protect the this structure access */ 61 rwlock_t lock; 62 //读写锁 63 64 /* 65 * This semaphore is used to ensure that files are not removed 66 * while epoll is using them. This is read-held during the event 67 * collection loop and it is write-held during the file cleanup 68 * path, the epoll file exit code and the ctl operations. 69 */ 70 struct rw_semaphore sem; 71 //读写信号量 72 73 /* Wait queue used by sys_epoll_wait() */ 74 wait_queue_head_t wq; 75 /* Wait queue used by file->poll() */ 76 77 wait_queue_head_t poll_wait; 78 /* List of ready file descriptors */ 79 80 struct list_head rdllist; 81 //已经完成的操作事件的队列。 82 83 /* RB-Tree root used to store monitored fd structs */ 84 struct rb_root rbr; 85 //保存epoll监视的文件描述符 86 }; 87 88 //这个结构体保存了epoll文件描述符的扩展信息,它被保存在file结构体的private_data 89 //中。它与epoll文件节点一一对应。通常一个epoll文件节点对应多个被监视的文件描述符。 90 //所以一个eventpoll结构体会对应多个epitem结构体。那么,epoll中的等待事件放在哪里呢?见下面 91 /* Wait structure used by the poll hooks */ 92 struct eppoll_entry { 93 /* List header used to link this structure to the "struct epitem" */ 94 struct list_head llink; 95 /* The "base" pointer is set to the container "struct epitem" */ 96 void *base; 97 /* 98 * Wait queue item that will be linked to the target file wait 99 * queue head.
100 */
101 wait_queue_t wait;
102 /* The wait queue head that linked the "wait" wait queue item */
103 wait_queue_head_t *whead;
104 };
105
106 //与select/poll的struct poll_table_entry相比,epoll的表示等待队列节点的结
107 //构体只是稍有不同,与struct poll_table_entry比较一下。
108 struct poll_table_entry {
109 struct file * filp;
110 wait_queue_t wait;
111 wait_queue_head_t * wait_address;
112 };
linux下epoll如何实现高效处理相关推荐
- Linux下epoll如何实现高效处理百万句柄的
开发高性能网络程序时,windows开发者们言必称iocp,linux开发者们则言必称epoll.大家都明白epoll是一种IO多路复用技术,可以非常高效的处理数以百万计的socket句柄,比起以前的 ...
- linux下Epoll实现简单的C/S通信
From: http://blog.csdn.net/piaojun_pj/article/details/6103709 epoll的优点: 1.支持一个进程打开大数目的socket描述符(FD) ...
- epoll nio区别_高性能网络服务器编程:为什么linux下epoll是最好,Netty要比NIO.2好?...
基本的IO编程过程(包括网络IO和文件IO)是,打开文件描述符(windows是handler,java是stream或channel),多路捕获(Multiplexe,即select和poll和ep ...
- linux socket epoll
什么是epoll epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll.当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new A ...
- Windows下完成端口移植Linux下的epoll
距离上一篇博客都已经半个多月了,这么多天一直在学习研究关于Windows的完成端口移植到Linux下epoll方面的内容.这两方面以前都没有太多的接触,所以花费了较长的时间.在连续加班两天后 ...
- 【流媒体服务器Mediasoup】 NodeJs与C++信令通信详解及Linux下管道通信的详解(五)
目录 前言 匿名管道进程间通信 进程间管道 的创建与图解 MediaSoup中的管道创建 MediaSoup Channel的创建 NodeJs和 C++ 管道通信的过程 MediaSoup 消息确认 ...
- Linux下select, poll和epoll IO模型的详解
http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...
- linux下编程epoll实现将GPS定位信息上报到服务器
操作系统:CentOS 开发板:fl2440 开发模块:A7(GPS/GPRS),RT3070(无线网卡) ********************************************** ...
- Linux 下 I/O 多路复用技术 epoll
Linux 下 I/O 多路复用技术 epoll 流?I/O操作?阻塞? 解决阻塞死等待的方法 方法一:阻塞 + 多进程 / 多线程. 方法二:非阻塞 + 忙轮询. 方法三:select(与平台无关) ...
最新文章
- jQuery Mobile发展新闻阅读器,适应iphone和android打电话
- 中点和中值滤波的区别_滤波器基础知识简介
- 关于IIS 7.0 局域网无法访问的解决方法 windows7 iis局域网不能访问
- linux删除文件夹提示没找到,Win10中遇到删除文件夹提示找不到该项目的解决过程...
- php websocket 实战,一次WebSocket项目实战后总结的经验
- 包无法安装_详细教程 | 安装Python编程环境以及使用OpenpyXl操作Excel
- linux oracle10.2.0.1 lsnrctl无法启动
- 360浏览器清除缓存_微信缓存清理教程
- “我没搞懂元宇宙,但一天能赚9w块”
- python paramiko并发_python paramiko 多线程批量执行指令及批量上传文件和目录
- 2019 蓝桥杯省赛 B 组模拟赛(一) 程序设计:后缀字符串 ( STL map 和 substr()函数的应用)
- 数据集:102 flower、Cratech256、ImageNet数据集下载
- 使用 webSocket 连接菜鸟打印(并获取当前电脑连接的打印机信息)
- GB50174-2008《电子信息系统机房设计规范》
- Adobe Dreamweaver CS5和Photoshop CS5序列号
- Win10免费升级win11方法
- 给定一个递增序列,a1 a2 ...an 。定义这个序列的最大间隔为d=max{ai+1 - ai }(1≤in),现在要从a2 ,a3 ..an-1 中删除一个元素。问剩余序列的最大间隔最小...
- 如何用人工的方式将Excel里的一堆数字变成一个数组
- FinalShell找回服务器密码
- 一文读懂Docker、K8s
热门文章
- c语言与硬件连接案例,实际LED硬件连接
- linux内核arch模块,Kernel module (简体中文)
- vim trick之 vimrc更改立即生效
- 子类super调用父类函数,该函数中调用的函数是父类还子类呢
- python无法识别vim中文代码
- moment获取几小时前_momentjs – 使用时刻在两个日期时间之间获得hh:mm的时差
- 论述类文本知识框架_考前知识梳理与答题技巧之论述类文本
- 机器人锤石下路组合_下周二,极智嘉研发总监讲解物流机器人视觉感知与定位关键技术...
- 主模式和野蛮模式_网络野蛮行为的含混性和观念
- 笔记-项目质量管理-精简