select poll epoll有什么区别
select/poll
select 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。
所以,对于 select 这种方式,需要进行 2 次「遍历」文件描述符集合,一次是在内核态里,一个次是在用户态里 ,而且还会发生 2 次「拷贝」文件描述符集合,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。
select 使用固定长度的 BitsMap,表示文件描述符集合,而且所支持的文件描述符的个数是有限制的,在 Linux 系统中,由内核中的 FD_SETSIZE 限制, 默认最大值为 1024,只能监听 0~1023 的文件描述符。
poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。
但是 poll 和 select 并没有太大的本质区别,都是使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合,这种方式随着并发数上来,性能的损耗会呈指数级增长。
epoll
epoll 的用法。如下的代码中,先用e poll_create 创建一个 epol l对象 epfd,再通过 epoll_ctl 将需要监视的 socket 添加到epfd中,最后调用 epoll_wait 等待数据。
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...);
listen(s, ...)int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中while(1) {int n = epoll_wait(...);for(接收到数据的socket){//处理}
}
epoll 通过两个方面,很好解决了 select/poll 的问题。
第一点,epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn)。而 select/poll 内核里没有类似 epoll 红黑树这种保存所有待检测的 socket 的数据结构,所以 select/poll 每次操作时都传入整个 socket 集合给内核,而 epoll 因为在内核维护了红黑树,可以保存所有待检测的 socket ,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。
第二点, epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。
从下图你可以看到 epoll 相关的接口作用

poll 的方式即使监听的 Socket 数量越多的时候,效率不会大幅度降低,能够同时监听的 Socket 的数目也非常的多了,上限就为系统定义的进程打开的最大文件描述符个数。因而,epoll 被称为解决 C10K 问题的利器。
插个题外话,网上文章不少说,epoll_wait 返回时,对于就绪的事件,epoll 使用的是共享内存的方式,即用户态和内核态都指向了就绪链表,所以就避免了内存拷贝消耗。
这是错的!看过 epoll 内核源码的都知道,压根就没有使用共享内存这个玩意。你可以从下面这份代码看到, epoll_wait 实现的内核代码中调用了 __put_user 函数,这个函数就是将数据从内核拷贝到用户空间。

边缘触发和水平触发
epoll 支持两种事件触发模式,分别是边缘触发(edge-triggered,ET)和水平触发(level-triggered,LT)。
这两个术语还挺抽象的,其实它们的区别还是很好理解的。
使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;
举个例子,你的快递被放到了一个快递箱里,如果快递箱只会通过短信通知你一次,即使你一直没有去取,它也不会再发送第二条短信提醒你,这个方式就是边缘触发;如果快递箱发现你的快递没有被取出,它就会不停地发短信通知你,直到你取出了快递,它才消停,这个就是水平触发的方式。
这就是两者的区别,水平触发的意思是只要满足事件的条件,比如内核中有数据需要读,就一直不断地把这个事件传递给用户;而边缘触发的意思是只有第一次满足条件的时候才触发,之后就不会再传递同样的事件了。
如果使用水平触发模式,当内核通知文件描述符可读写时,接下来还可以继续去检测它的状态,看它是否依然可读或可写。所以在收到通知后,没必要一次执行尽可能多的读写操作。
如果使用边缘触发模式,I/O 事件发生时只会通知一次,而且我们不知道到底能读写多少数据,所以在收到通知后应尽可能地读写数据,以免错失读写的机会。因此,我们会循环从文件描述符读写数据,那么如果文件描述符是阻塞的,没有数据可读写时,进程会阻塞在读写函数那里,程序就没办法继续往下执行。所以,边缘触发模式一般和非阻塞 I/O 搭配使用,程序会一直执行 I/O 操作,直到系统调用(如 read 和 write)返回错误,错误类型为 EAGAIN 或 EWOULDBLOCK。
一般来说,边缘触发的效率比水平触发的效率要高,因为边缘触发可以减少 epoll_wait 的系统调用次数,系统调用也是有一定的开销的的,毕竟也存在上下文的切换。
select/poll 只有水平触发模式,epoll 默认的触发模式是水平触发,但是可以根据应用场景设置为边缘触发模式。
另外,使用 I/O 多路复用时,最好搭配非阻塞 I/O 一起使用,Linux 手册关于 select 的内容中有如下说明:
在Linux下,select() 可能会将一个 socket 文件描述符报告为 “准备读取”,而后续的读取块却没有。例如,当数据已经到达,但经检查后发现有错误的校验和而被丢弃时,就会发生这种情况。也有可能在其他情况下,文件描述符被错误地报告为就绪。因此,在不应该阻塞的 socket 上使用 O_NONBLOCK 可能更安全。
简单点理解,就是多路复用 API 返回的事件并不一定可读写的,如果使用阻塞 I/O, 那么在调用 read/write 时则会发生程序阻塞,因此最好搭配非阻塞 I/O,以便应对极少数的特殊情况。
总结
最基础的 TCP 的 Socket 编程,它是阻塞 I/O 模型,基本上只能一对一通信,那为了服务更多的客户端,我们需要改进网络 I/O 模型。
比较传统的方式是使用多进程/线程模型,每来一个客户端连接,就分配一个进程/线程,然后后续的读写都在对应的进程/线程,这种方式处理 100 个客户端没问题,但是当客户端增大到 10000 个时,10000 个进程/线程的调度、上下文切换以及它们占用的内存,都会成为瓶颈。
为了解决上面这个问题,就出现了 I/O 的多路复用,可以只在一个进程里处理多个文件的 I/O,Linux 下有三种提供 I/O 多路复用的 API,分别是:select、poll、epoll。
select 和 poll 并没有本质区别,它们内部都是使用「线性结构」来存储进程关注的 Socket 集合。
在使用的时候,首先需要把关注的 Socket 集合通过 select/poll 系统调用从用户态拷贝到内核态,然后由内核检测事件,当有网络事件产生时,内核需要遍历进程关注 Socket 集合,找到对应的 Socket,并设置其状态为可读/可写,然后把整个 Socket 集合从内核态拷贝到用户态,用户态还要继续遍历整个 Socket 集合找到可读/可写的 Socket,然后对其处理。
很明显发现,select 和 poll 的缺陷在于,当客户端越多,也就是 Socket 集合越大,Socket 集合的遍历和拷贝会带来很大的开销,因此也很难应对 C10K。
epoll 是解决 C10K 问题的利器,通过两个方面解决了 select/poll 的问题。
epoll 在内核里使用「红黑树」来关注进程所有待检测的 Socket,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn),通过对这棵黑红树的管理,不需要像 select/poll 在每次操作时都传入整个 Socket 集合,减少了内核和用户空间大量的数据拷贝和内存分配。
epoll 使用事件驱动的机制,内核里维护了一个「链表」来记录就绪事件,只将有事件发生的 Socket 集合传递给应用程序,不需要像 select/poll 那样轮询扫描整个集合(包含有和无事件的 Socket ),大大提高了检测的效率。
而且,epoll 支持边缘触发和水平触发的方式,而 select/poll 只支持水平触发,一般而言,边缘触发的方式会比水平触发的效率高。
相关文章:
select poll epoll有什么区别
select/poll select 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述…...
Java基础面试题突击系列1
👩🏻 作者:一只IT攻城狮 ,关注我不迷路 ❤️《java面试核心知识》突击系列,持续更新… 💐 面试必知必会学习路线:Java技术栈面试系列SpringCloud项目实战学习路线 📝再小的收获x365天…...
go-zero和dtm分布式事务实现
go-zero是一个基于Go语言的微服务开发框架,而DTM(Distributed Transaction Manager)是一个分布式事务管理器,用于实现跨多个微服务的分布式事务。 下面是使用go-zero和DTM实现分布式事务的基本步骤: 安装和配置DTM&am…...
Springboot +Flowable,会签、或签简单使用(一)
一.简介 **会签:**在一个流程中的某一个 Task 上,这个 Task 需要多个用户审批,当多个用户全部审批通过,或者多个用户中的某几个用户审批通过,就算通过。 例如:之前的请假流程,假设这个请假流程…...
银行面试中的即兴演讲技巧,你了解吗
面试的主要形式有无领导小组讨论、结构化和半结构化面试,一些银行还会出现辩论赛、角色扮演、即兴演讲等形式,今天小编就来聊一聊面试中的即兴演讲,从如信银行考试中心了解到: 简单来说即兴演讲就是在特定情境下,自发或…...
基于LT6911UXC的LVDS视频接口调试经验
文章目录 概要整体架构流程技术名词解释技术细节小结概要 项目方案采用龙讯半导体LT6911系列 整体架构流程 CPU主芯片采用飞腾,显卡采用GP102,由于项目需要LVDS接口,GP102的HDMI口输出后经过LT6911UXC转成LVDS输出 在本方案中有两种显示屏;一个是10.4寸,一个是15.6寸。…...
【C++】OpenMP简介
一、背景知识 1、program作用 #pragma 是 C 和 C 语言中的一个预处理指令。它主要用于向编译器发送特定的信息或者指示,以便控制编译过程。#pragma 通常与编译器特定的选项或功能一起使用,因此它的具体行为和实现可能因不同的编译器而有所不同。 #pra…...
Unity HybridCLR 热更工具学习日记(一)
目录 导入HybridCLR包、安装设置相关选项 导入HybridCLR包 先找到HybridCLR包的git地址:https://github.com/focus-creative-games/hybridclr 复制包的http地址,打开unity - window - package Manager;点击左上角的 选择Add Package for…...
内网渗透(七十五)之域权限维持之DCShadow
DCShadow 2018年1月24日,在BlueHat安全会议上,安全研究员Benjamin Delpy 和 Vincent Le Toux 公布了针对微软活动目录域的一种新型攻击技术------DCShaow。利用该攻击技术,具有域管理员权限或企业管理员权限的恶意攻击者可以创建恶意域控,然后利用域控间正常同步数据的功能…...
JAVA日常练习—习题5
1.机器人类及其子类 设计一个机器人的抽象类,具有质量(double)、名字(String)等属性;具有设置属性值方法和获得并打印属性值的方法,还有一个运动的抽象方法move()。设计电动汽车类和飞行器类&a…...
nuitka打包python的PyQt5成exe可执行文件
使用nuitka打包python的PyQt5 可以打包多文件,也可以打包单文件。 使用的python版本是3.8.10 1、下载gcc 方式1:可以到网站下载,点我跳转 方式2:可以直接去网盘下载,点我跳转 提取码:8888 网盘里面也有python3.8.10的安装包,下载后直接安装即可。 下载64位 win…...
数据(安全)治理面临的挑战和机遇
数据治理,数据安全治理面临的挑战就是该市场的机遇,国内外相关实践和成功的商业案例是存在的,国内目前实施主要不在考虑主体数据或另开一个新项目进行数据采集,清洗,展示,但很少有真正的通过深度学习工具提升企业运营效能,正如我上一篇(湖仓智能应用简论-数据驱动设计_…...
Java后端入职第三天,使用CompletableFuture优化查询接口
一、需求背景 相信大家入职到一个新公司(xinkeng),一般都会被分配去解决千古难题(caipigu),这些问题大概率很多不想去解决,并且代码很多复杂,各种套娃。领导为检验新人的能力,就会去让新人去解决这种问题,一方面可以了解业务代码,还能顺手把没人愿意做的事做了。但…...
卷积神经网络实例
文章目录 1. 数据输入2. 创建卷积模型并训练3. 超参数选择 卷积神经网络(CNN)主要由卷积层(Convolutional Layer)、池化层(Pooling Layer)、激活层(Activation Layer)和全连接层(Fully-connected Layer)组成。 卷积层(Convolutional Layer):卷积层是CNN的核心组成部…...
LayerZero有何发展潜力?空投热潮和大额融资双重加持
前言 近期Arbitrum的如愿空投再次点燃了市场「刷空投」的热情,除了ZK系的zkSync、Starknet及Scroll,也有部分用户将注意力投向了估值30亿美元的LayerZero。而 LayerZero刚刚完成的1.2亿美元B轮融资也让其市场热度持续攀升,在「空投热潮」及「…...
深度学习之构建MPL神经网络——泰坦尼克号乘客的生存分析
大家好,我是带我去滑雪! 本期使用泰坦尼克号数据集,该数据集的响应变量为乘客是生存还是死亡(survived,其中1表示生存,0表示死亡),特征变量有乘客舱位等级(pclass&#x…...
一条神奇的sql
背景:人脸闸机,每刷一次人脸,就会有一条记录插入到通行记录表。而闸机可能会多次识别同一个人的人脸,那么这时通行记录表就会插入多次同一个人的记录,同一个人的记录中,只不过通行时间不同而已 需求&#…...
数据结构总结3:栈和队列
后续会有补充和更改 栈和队列 栈和队列也属于线性表 栈 一种特殊的线性表,只允许在固定的一端进行插入和删除元素。该端称为栈顶,另一端称为栈底。 栈中的数据遵循后进先出(LIFO)的原则 压栈/进栈/入栈:数据插入…...
私有化部署的即时通讯软件:消息、文件安全加密,全面可控
如今,数字化转型进入纵深阶段,在企业数字化转型过程中,数据规模激增,结构更为复杂,数据零散化和安全性问题日益显著,使得众多企业在数据资产管理上面临不小的挑战。企业为提高内部沟通效率,通常…...
27-Django项目实战(5)
1 歌曲搜索 音乐平台的每个网页顶部都设置了歌曲搜索功能,歌曲搜索框以网页表单的形式展示,并且以POST请求方式实现歌曲搜索功能,搜索结果显示在歌曲搜索页。歌曲搜索页由项目应用search实现,首先在search的urls.py中定义路由sea…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
