Linux中select poll和epoll的区别

news2024/10/7 2:22:01

在Linux Socket服务器短编程时,为了处理大量客户的连接请求,需要使用非阻塞I/O和复用,select、poll和epoll是Linux API提供的I/O复用方式,自从Linux 2.6中加入了epoll之后,在高性能服务器领域得到广泛的应用,现在比较出名的nginx就是使用epoll来实现I/O复用支持高并发。

select:

下面是select的函数接口:

intselect (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。

poll:

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。

struct pollfd {int fd; /* file descriptor */short events; /* requested events to watch */short revents; /* returned events witnessed */};

pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

epoll:

epoll的接口如下:

int epoll_create(int size);int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t; struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ };int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

主要是epoll_create,epoll_ctl和epoll_wait三个函数。epoll_create函数创建epoll文件描述符,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。返回是epoll描述符。-1表示创建失败。epoll_ctl 控制对指定描述符fd执行op操作,event是与fd关联的监听事件。op操作有三种:添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分别添加、删除和修改对fd的监听事件。epoll_wait 等待epfd上的io事件,最多返回maxevents个事件。

在 select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一 个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait() 时便得到通知。

epoll的优点主要是一下几个方面:

1. 监视的描述符数量不受限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。select的最大缺点就是进程打开的fd是有数量限制的。这对 于连接数量比较大的服务器来说根本不能满足。虽然也可以选择多进程的解决方案( Apache就是这样实现的),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也 不是一种完美的方案。

2. IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。

3.支持电平触发和边沿触发(只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发)两种方式,理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

4.mmap加速内核与用户空间的信息传递。epoll是通过内核于用户空间mmap同一块内存,避免了无畏的内存拷贝。

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。关于这三种IO多路复用的用法,前面三篇总结写的很清楚,并用服务器回射echo程序进行了测试。连接如下所示:

select:IO多路复用之select总结 - Rabbit_Dale - 博客园

poll:IO多路复用之poll总结 - Rabbit_Dale - 博客园

epoll:IO多路复用之epoll总结 - Rabbit_Dale - 博客园

  今天对这三种IO多路复用进行对比,参考网上和书上面的资料,整理如下:

1、select实现

select的调用过程如下所示:

(1)使用copy_from_user从用户空间拷贝fd_set到内核空间

(2)注册回调函数__pollwait

(3)遍历所有fd,调用其对应的poll方法(对于socket,这个poll方法是sock_poll,sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll)

(4)以tcp_poll为例,其核心实现就是__pollwait,也就是上面注册的回调函数。

(5)__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了。

(6)poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值。

(7)如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout是调用select的进程(也就是current)进入睡眠。当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(schedule_timeout指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd。

(8)把fd_set从内核空间拷贝到用户空间。

补充:如果没有I/O事件产生,我们的程序就会阻塞在select处。但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,但却并不知道是那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。但是使用select,我们有O(n)的无差别轮询复杂度,同时处理的流越多,没一次无差别轮询时间就越长。

总结:

select的几大缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小了,默认是1024

2 poll实现

  poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多。

关于select和poll的实现分析,可以参考下面几篇博文:

select(poll)系统调用实现解析(一)_zgolee的博客-CSDN博客

select(poll)系统调用实现解析(二)_select.pollin_zgolee的博客-CSDN博客

select(poll)系统调用实现解析(三)_zgolee的博客-CSDN博客

IBM Developer

http://linux.chinaunix.net/techdoc/net/2009/05/03/1109887.shtml

3、epoll

  epoll既然是对select和poll的改进,就应该能避免上述的三个缺点。那epoll都是怎么解决的呢?在此之前,我们先看一下epoll和select和poll的调用接口上的不同,select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。

  对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。

  对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。

  对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

总结:

(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

参考资料:

select 实现分析 –2 【整理】 - Apprentice89 - 博客园

select,poll,epoll实现分析—结合内核源代码 第3页_Linux编程_Linux公社-Linux系统门户网站

http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/

select、poll、epoll使用小结_poll pollpri_bigxu的博客-CSDN博客

https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c

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

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

相关文章

Fastchat:基于fastapi构建大模型加载服务

Fastchat:基于fastapi构建大模型加载服务 Fastapi LLM支持FastChat 服务架构安装方法一:pip方法二:源文件 加载大模型推理1.命令行2.Web启动控制器启动模型工作启动 Gradio Web 服务器注册多个模型 3.兼容 OpenAI 的 RESTful API RESTful API…

CSS 滚动驱动动画 view()

CSS 滚动驱动动画 view 语法例子 两个 inset一个 inset CSS 滚动驱动动画 view() animation-timeline 通过 view() 表示一个元素 A 将提供匿名的、 用来控制动画的 view progressive timeline. 通过 A 在其最近的滚动祖先元素中的可见性来推动 view progressive timeline. 也…

PHP自己的框架2.0版本目录结构和命名空间自动加载类(重构篇一)

目录 1、目录结构演示效果 2、搭建目录结构,以及入口public->index.php 3、引入core下面core->base.php 4、自动加载实现core->fm->autoload.php 5、框架运行文件core->fm->core.php 6、最终运行index.php结果 1、目录结构演示效果 2、搭…

单目标应用:基于成长优化算法(Growth Optimizer,GO)的微电网优化调度MATLAB

一、微网系统运行优化模型 微电网是由分布式电源、储能装置和能量转换装置等组成的小型发配电系统,具有成本低、电压低、污染小等特点。由于环保和能源压力,清洁可再生能源和分布式能源工业发展潜力巨大。微电网控制器可实现对电网的集中控制&#xff0…

【C++笔记】C++string类模拟实现

【C笔记】Cstring类模拟实现 一、实现模型和基本接口1.1、各种构造和析构1.2、迭代器 二、各种插入和删除接口2.1、插入接口2.2、删除接口2.3、resize接口 三、各种运算符重载3.1、方括号运算符重载3.2、各种比较运算符重载 四、查找接口4.1、查找字符4.2、查找子串 五、流插入…

【7z密码】如何给7z压缩包加密、解密?

7z压缩包是压缩率最大的格式,也有很多朋友会使用7z格式,那么7z压缩包如何进行加密、解密?今天给大家介绍详细教程。 7-zip加密 右键文件选择7-zip打开压缩软件进行压缩或者在打开7-zip软件找到需要压缩的文件,点击添加&#xff…

【大数据】基于 Flink CDC 高效构建入湖通道

基于 Flink CDC 高效构建入湖通道 1.Flink CDC 核心技术解析2.CDC 数据入湖入仓的挑战2.1 CDC 数据入湖架构2.2 CDC 数据 ETL 架构 3.基于 Flink CDC 的入湖入仓方案3.1 Flink CDC 入湖入仓架构3.2 Flink CDC ETL 分析3.3 存储友好的写入设计3.4 Flink CDC 实现异构数据源集成3…

ECharts数据可视化项目

Echarts 可视化面板介绍01-使用技术02- 案例适配方案03-基础设置04-header 布局05-mainbox 主体模块06-公共面板模块 panel07-柱形图 bar 模块(布局)08-中间布局09-Echarts-介绍10-Echarts-体验11-Echarts-基础配置12- 柱状图图表(两大步骤)13-柱状图2定…

基于Java+SpringBoot+Vue前后端分离交通管理在线服务系统设计和实现

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

地形有通挂支隘险远六种情况

地形有通、挂、支、隘、险、远六种情况 【安志强趣讲《孙子兵法》第34讲】 第十一篇:地形篇 【全文大白话】 地形有各种情况,行军有各种情况,用好地形获得交战的主动权。 【原文】 孙子曰:地形有通者,有挂者&#xff0…

百家号创业项目:小白即可日入30到100,寻找爆款文案技术教程免费分享

百家号项目:月入1K-3K不费力,寻找爆款文案的秘诀! 当下,有一个备受关注的项目——百家号,许多人以 300-800 元不等的价格提供培训,声称每个号每月可以轻松赚取1K-3K。 然而,据我个人测试&…

容器编排学习(一)容器技术

一 容器 1 Linux 容器的起源 容器的起源可以追溯到1979年 UNIX 系统中提供的chroot命令,容器的最初的设计目标是为了隔离计算机中的各类资源以便降低软件开发、测试阶段的风险,或者充当蜜罐,吸引黑客的攻击,以便监视黑客的行为…

Logstash--logstash-syslog-putput插件安装及使用

这篇文章讲的是如何在Linux服务器上安装logstash-syslog-output插件及使用,是集网上之大成,择选出一条正确有效简短的路。 安装 插件logstash-syslog-output,如果你的logstash没有,则需要安装。 查看logstash是否含有这个插件的…

代码生成商业化一些思考

代码生成解决方案 生成项目代码有3大类的解决思路: 1.从底到上的生成,部分代码生成生成一行代码或者一个方法种一小块代码生成,ide插件代码生成基本这种思路 2.大语言模型作为软件工程不同角色agent,用户给出idea每个agent自动…

vue3:16、Pinia的基本语法

选项式APi 组合式API src/store/counter.js import { defineStore } from "pinia"; import { computed, ref } from "vue";export const userCounterStore defineStore("counter",()>{//声明数据 state - countconst count ref(100)//声…

ATTENTION, LEARN TO SOLVE ROUTING PROBLEMS!

ATTENTION, LEARN TO SOLVE ROUTING PROBLEMS! 1、背景2、基于注意力层的模型4、解码器 1、背景 本篇论文基于Transformer模型提出了一个基于注意力层的模型,并采用REINFORCE方法训练模型,来求解以下几种组合优化问题: 旅行商问题(Travellin…

Docker 的分层文件系统

1 分层文件系统 UnionFS 联合文件系统 bootfs:boot file systemrootfs:root file system 分层文件系统 Docker镜像都是只读的,当容器启动时,一个新的可写层被加到镜像的顶部,这一层就是我们通常说的容器层&#xf…

Redis缓存预热、缓存雪崩、缓存击穿、缓存穿透

文章目录 Redis缓存预热、缓存雪崩、缓存击穿、缓存穿透一、缓存预热1、问题排查2、解决方案(1)准备工作(2)实施(3)总结 二、缓存雪崩1、解决方案 三、缓存击穿1、解决方案(1)互斥锁…

大语言模型之八-提示工程(Prompt engineer)

大语言模型的效果好,很大程度上归功于算力和数据集,算力使得模型可以足够大,大到模型的理解、记忆、推理和生成以及泛化能力可以同时兼备,而数据集则是模型学习的来源。 LLM中的prompt方法主要用于fine-tune阶段,即在…

DIM层维度表学习之用户维度表分析

1.用户维度表的模型 DROP TABLE IF EXISTS dim_user_zip; CREATE EXTERNAL TABLE dim_user_zip (id STRING COMMENT 用户ID,name STRING COMMENT 用户姓名,phone_num STRING COMMENT 手机号码,email STRING COMMENT 邮箱,user_level STRING COM…