【IO多路复用】IO机制、select、poll、epoll、边缘触发、水平触发等重点知识汇总

目录

多路复用

Linux有哪些IO机制

select、poll、epoll 区别

select、poll、epoll 各自的优缺点

边缘触发ET和水平触发LT


多路复用

IO多路复用是一种处理多个IO流的技术。

它允许单个进程同时监视多个文件描述符,当一个或多个文件描述符准备好读或写时,它就可以立即响应。这种技术可以提高系统的并发性和响应能力,减少系统资源的浪费。

在Linux中,epoll、select、poll都是IO多路复用的实现方式,他们都可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回产生了事件的连接,然后在用户态中再处理这些连接对应的请求即可。

IO多路复用的优点包括:

  • 节省资源:不需要为每个连接创建一个线程或进程,因此节省了大量的系统资源,避免了线程或进程的开销。
  • 高性能:允许同时处理大量的IO操作,从而提高了系统的并发处理能力和性能。

Linux有哪些IO机制

阻塞IO:应用程序在进行IO操作时,会一直阻塞等待IO完成,期间无法进行其他操作。

非阻塞IO:应用程序在进行IO操作时,会立即返回,无论IO操作是否完成,应用程序都可以进行其他操作。需要通过轮询的方式来判断IO是否完成,因此效率较低。

IO多路复用:通过select、poll、epoll等系统调用,在一个进程中可以同时监控多个文件描述符,当有任何一个文件描述符就绪时,就可以进行IO操作。

信号驱动IO:应用程序在进行IO操作时,向内核注册一个信号处理函数,内核在IO完成时会向应用程序发送一个信号,应用程序收到信号后再进行数据处理。

异步IO:应用程序进行IO操作时,可以立即返回,内核负责将数据读取到指定的缓冲区中,并在完成后通知应用程序,应用程序可以继续进行其他操作。异步IO需要操作系统和硬件的支持,目前主要应用于高性能IO场景。

select、poll、epoll 区别

最基础的 TCP 的 Socket 编程,它是阻塞 I/O 模型,基本上只能一对一通信,那为了服务更多的客户端,我们需要改进网络 I/O 模型。

比较传统的方式是使用多进程/线程模型,每来一个客户端连接,就分配一个进程/线程,然后后续的读写都在对应的进程/线程,这种方式处理 100 个客户端没问题,但是当客户端增大到 10000 个时,10000 个进程/线程的调度、上下文切换以及它们占用的内存,都会成为瓶颈。

为了解决上面这个问题,就出现了 I/O 的多路复用,可以只在一个进程里处理多个文件的 I/O,Linux 下有三种提供 I/O 多路复用的 API,分别是:select、poll、epoll

select 和 poll 并没有本质区别,它们内部都是使用「线性结构」来存储进程关注的 Socket 集合。

select通常受到文件描述符数量的限制,由FD_SETSIZE(通常是1024)定义。

poll没有文件描述符数量的限制,可以处理比select更多的文件描述符。

在多个平台上都可用,具有较好的可移植性。

在使用的时候,首先需要把关注的 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 ),大大提高了检测的效率。

仅在Linux系统上可用,不具有可移植性,但在Linux上性能卓越。适用于高并发的网络服务器

而且,epoll 支持边缘触发和水平触发的方式,而 select/poll 只支持水平触发,一般而言,边缘触发的方式会比水平触发的效率高。

那如果服务器的内存只有 2 GB,网卡是千兆的,能支持并发 1 万请求吗?

并发 1 万请求,也就是经典的 C10K 问题 ,C 是 Client 单词首字母缩写,C10K 就是单机同时处理 1 万个请求的问题。

select、poll、epoll 各自的优缺点

select优缺点

优点:

  • 在多个平台上都可用,具有较好的可移植性。
  • 适用于小规模连接,对于文件描述符数量不太多的情况性能还可以接受。
  • 简单易用,不需要特殊的数据结构。

缺点:

  • select() 检测数量有限制,最大值通常为 1024(bit),每一个比特位对应一个监听的文件描述符,如果您尝试监听超过这个数量的文件描述符,可能会导致未定义的行为或错误。
  • 一旦select调用返回,fd_set中的位将反映哪些文件描述符发生了IO事件,已经被内核修改。因此,您需要在每次调用select之前重新设置fd_set,以重新指定要监听的文件描述符。
  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大(时间复杂度是O(n))

poll优缺点

优点:

  • 类似于select,也具有一定的可移植性。
  • 没有文件描述符数量的限制,理论上可以处理更多的文件描述符。
  • 不需要像select一样重新设置文件描述符集合,因为它在每次调用时都使用数组。

缺点:

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 不能直接返回哪些文件描述符发生了事件,需要在返回后遍历数组来查找。
  • 每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大(时间复杂度是O(n))
  • 随着文件描述符数量的增加,性能可能下降,因为每次调用poll仍然需要遍历整个文件描述符数组。
  • 不能直接返回哪些文件描述符发生了事件,需要在返回后遍历数组来查找。
  • 也可能受到高并发连接的限制。

epoll优缺点

优点:

  • 仅在Linux系统上可用,不具有可移植性,但在Linux上性能卓越。
  • 没有文件描述符数量的限制,可以处理大规模的并发连接。
  • 使用一个事件表来存储已注册的文件描述符和相关的IO事件,不需要在每次调用时重新设置监听。
  • 可以以水平触发(LT,Level-Triggered)或边缘触发(ET,Edge-Triggered)两种工作模式运行。
  • 返回只有发生IO事件的文件描述符,避免了不必要的遍历。
  • epoll底层数据结构很好,红黑树增删改综合效率高
  • 就绪的描述符的链表。当有的连接就绪的时候,内核会把就绪的连接放到 rdllist 链表里。这样应用进程只需要判断链表就能找出就绪进程,而不用去遍历整棵树。

缺点:

  • 不具备跨平台可移植性,仅适用于Linux系统。
  • 有时需要维护较复杂的数据结构来管理事件表。

边缘触发ET和水平触发LT

epoll 支持两种事件触发模式,分别是边缘触发(edge-triggered,ET)和水平触发(level-triggered,LT

  • 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
  • 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;

举个例子,你的快递被放到了一个快递箱里,如果快递箱只会通过短信通知你一次,即使你一直没有去取,它也不会再发送第二条短信提醒你,这个方式就是边缘触发;如果快递箱发现你的快递没有被取出,它就会不停地发短信通知你,直到你取出了快递,它才消停,这个就是水平触发的方式。

这就是两者的区别,

水平触发的意思是只要满足事件的条件,比如内核中有数据需要读,就一直不断地把这个事件传递给用户;

边缘触发的意思是只有第一次满足条件的时候才触发,之后就不会再传递同样的事件了。

如果使用水平触发模式,当内核通知文件描述符可读写时,接下来还可以继续去检测它的状态,看它是否依然可读或可写。所以在收到通知后,没必要一次执行尽可能多的读写操作。

如果使用边缘触发模式,I/O 事件发生时只会通知一次,而且我们不知道到底能读写多少数据,所以在收到通知后应尽可能地读写数据,以免错失读写的机会。因此,我们会循环从文件描述符读写数据,那么如果文件描述符是阻塞的,没有数据可读写时,进程会阻塞在读写函数那里,程序就没办法继续往下执行。所以,边缘触发模式一般和非阻塞 I/O 搭配使用,程序会一直执行 I/O 操作,直到系统调用(如 read 和 write)返回错误,错误类型为EAGAINEWOULDBLOCK

一般来说,边缘触发的效率比水平触发的效率要高,因为边缘触发可以减少 epoll_wait 的系统调用次数,系统调用也是有一定的开销的的,毕竟也存在上下文的切换。

select/poll 只有水平触发模式,epoll 默认的触发模式是水平触发,但是可以根据应用场景设置为边缘触发模式。

另外,使用 I/O 多路复用时,最好搭配非阻塞 I/O 一起使用,Linux 手册关于 select 的内容中有如下说明:

在Linux下,select() 可能会将一个 socket 文件描述符报告为 "准备读取",而后续的读取块却没有。例如,当数据已经到达,但经检查后发现有错误的校验和而被丢弃时,就会发生这种情况。也有可能在其他情况下,文件描述符被错误地报告为就绪。因此,在不应该阻塞的 socket 上使用 O_NONBLOCK 可能更安全。

简单点理解,就是多路复用 API 返回的事件并不一定可读写的,如果使用阻塞 I/O, 那么在调用 read/write 时则会发生程序阻塞,因此最好搭配非阻塞 I/O,以便应对极少数的特殊情况。

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

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

相关文章

c++堆排序-建堆-插入-删除-排序

本文以大根堆为例&#xff0c;用数组实现&#xff0c;它的nums[0]是数组最大值。 时间复杂度分析&#xff1a; 建堆o(n) 插入删除o(logn) 堆排序O(nlogn) 首先上代码 #include<bits/stdc.h>using namespace std; void down(vector<int>&nums, int idx, i…

SpringMVC 学习(七)JSON

9. JSON 9.1 简介 JSON&#xff08;JavaScript Object Notation&#xff0c;JS 对象标记&#xff09;是一种轻量级数据交换格式&#xff0c;采用独立于编程语言的文本格式储存和表示数据&#xff0c;易于机器解析和生成&#xff0c;提升网络传输效率。 任何 JavaScript 支持…

Midjourney 生成油画技巧

基本 prompt oil painting, a cute corgi dog surrounded with colorful flowers技法 Pointillism 点描绘法 笔刷比较细&#xff0c;图像更精细 oil painting, a cute corgi dog surrounded with colorful flowers, pontillismImpasto 厚涂绘法 笔刷比较粗&#xff0c;图像…

将切分的图片筛选出有缺陷的

将切分的图片筛选出有缺陷的 需求代码 需求 由于之前切分的图像有一些存在没有缺陷&#xff0c;需要再次筛选 将可视化的图像更改后缀 更改为xml的 可视化代码 可视化后只有7000多个图像 原本的图像有1W多张 代码 # 按照xml文件删除对应的图片 # coding: utf-8 from P…

unittest单元测试框架使用

什么是unittest 这里我们将要用的unittest是python的单元测试框架&#xff0c;它的官网是 25.3. unittest — Unit testing framework — Python 2.7.18 documentation&#xff0c;在这里我们可以得到全面的信息。 当我们写的用例越来越多时&#xff0c;我们就需要考虑用例编写…

LiveData源码分析

1.在上一篇文章中介绍了LiveData的简单使用&#xff0c;可以查看LiveData简单使用_niuyongzhi的博客-CSDN博客 源码分析&#xff1a; 也可以分成两部分来看&#xff0c;一部分是注册observe源码&#xff0c;另一部分是setValue的源码。 先看Observe注册监听的方法。 1&…

Redis实现Session持久化

Redis实现Session持久化 1. 前言 直接使用Session存储用户登录信息&#xff0c;此时的会话信息是存储在内中的&#xff0c;只要项目重启存储的Session信息就会丢失。而使用Redis存储Session的话就不会存在这种情况&#xff0c;即使项目重启也并不影响&#xff0c;也无需用户重…

GIT提示Another git process seems to be running in this repository

解决方法 1、进入项目里面的.git文件里面找到index.lock删除即可。

掷骰子的多线程应用程序2基于互斥量的线程同步(复现《Qt C++6.0》)

说明&#xff1a;在复现过程中出现两点问题&#xff08;1&#xff09;run()函数中对m_diceValued的赋值&#xff08;2&#xff09;do_timeOut()函数中没有对m_seq、m_diceValued进行定义。修改后的复现程序如下所示&#xff1a; 主线程&#xff1a; .h #pragma once#include…

PostgreSQL 查询某个属性相同内容出现的次数

查询某个数据库表属性 name 相同内容出现出现的次数&#xff0c;并按次数从大到小排序 SELECT name, COUNT(*) AS count FROM your_table GROUP BY name ORDER BY count DESC;示例 select project_id, COUNT(*) AS count from app_ads_positions group by project_id order b…

民安智库(专业市场调查公司)家居行业消费者调查如何做?

随着人们生活水平的提高&#xff0c;家居行业的发展日益蓬勃。在家居行业竞争激烈的市场环境下&#xff0c;了解消费者的需求和喜好至关重要。为了更好地了解家居消费者的需求和习惯&#xff0c;为家居品牌方提供有力的市场支持&#xff0c;民安智库通过本文介绍家居行业消费者…

mysql Your password does not satisfy the current policy requirements

在修改密码时遇到 Your password does not satisfy the current policy requirements 原因&#xff1a;您的密码不符合当前策略要求&#xff0c;最好是把密码设置成复杂的&#xff0c;包括字母大小写、数字、特殊字符。 如果你还是先把数据库密码改简单&#xff0c;比如你本地…

Redis 集合操作实战(全)

目录 SADD 插入集合 SCARD 取元素数量 SPOP 随机移除元素 SREM 移除多个元素 SMOVE 移动元素到别的集合 SMEMBERS 取所有成员 SRANDMEMBER 取指定数量元素 SISMEMBER 判断元素是否存在 SUNION 多集合求并集 SUNIONSTORE 多集合求并集&#xff08;存储&#xff09; S…

vue点击按钮收缩菜单

问题描述 VUE菜单有一个BUG&#xff0c;当我们点击其它按钮或者首页的时候&#xff0c;已经展示的一级菜单是不会自动收缩的。这个问题也导致很多开发者把一级菜单都换成了二级菜单。 错误展示 错误的效果请看下图。 解决方法 1、寻找菜单文件 因为我使用的是ruoyi的前端框…

Spring Controller内存马

获取当前上下文运行环境 getCurrentWebApplicationContext WebApplicationContext context ContextLoader.getCurrentWebApplicationContext(); 在SpringMVC环境下获取到的是一个XmlWebApplicationContext类型的Root WebApplicationContext&#xff1a; 在Spring MVC环境中…

可信执行环境(Tee)入门综述

SoK: Hardware-supported Trusted Execution Environments [ArXiv22] 摘要引言贡献 范围系统和威胁模型系统模型威胁模型共存飞地对手无特权软件对手系统软件对手启动对手外围对手结构对手侵入性对手 关于侧信道攻击的一点注记 VERIFIABLE LAUNCH信任根&#xff08;RTM&#xf…

8月最新修正版风车IM即时聊天通讯源码+搭建教程

8月最新修正版风车IM即时聊天通讯源码搭建教程。风车 IM没啥好说的很多人在找,IM的天花板了,知道的在找的都知道它的价值,开版好像就要29999,后端加密已解,可自己再加密,可反编译出后端项目源码,已增加启动后端需要google auth双重验证,pc端 web端 wap端 android端 ios端 都有 …

【Java 进阶篇】数据定义语言(DDL)详解

数据定义语言&#xff08;DDL&#xff09;是SQL&#xff08;结构化查询语言&#xff09;的一部分&#xff0c;它用于定义、管理和控制数据库的结构和元素。DDL允许数据库管理员、开发人员和其他用户创建、修改和删除数据库对象&#xff0c;如表、索引、视图等。在本文中&#x…

100万级连接,石墨文档WebSocket网关如何架构?

说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;很多小伙伴拿到一线互联网企业如阿里、网易、有赞、希音、百度、滴滴的面试资格。 最近&#xff0c;尼恩指导一个小伙伴简历&#xff0c;写了一个《高并发网关项目》&#xff0c;此项目帮这个小伙拿到 字节/阿里/…

【量化】量化原理浅析

前言 模型在端侧运行时&#xff0c;会追求模型保持原有精度的同时&#xff0c;让模型的运行速度更快。基本方向为模型压缩和加速&#xff0c;着力于减少网络参数量、降低计算复杂度。可通过以下方式实现&#xff1a; 针对网络结构本身进行改进&#xff0c;常用的3x3的卷积的叠加…