C++ Webserver从零开始:基础知识(四)——I/O复用

目录

前言

select系统调用

poll系统调用

epoll系统调用

epoll_create

epoll_ctl

epoll_wait

LT和ET模式

EPOLLONESHOT事件

epoll和select/poll的区别

事件集处理方式

实现原理和效率

其他区别


前言

        在第三章中我们大概地讲解了什么是I/O复用,即:I/O复用技术即使用select,poll,epoll等系统调用,让主线程能同时监听多个文件描述符,提高服务器运行的效率。

        PS: 虽然I/O复用能同时监听多个文件描述符,但是其本身是阻塞的,当主线程已经监听到多个文件描述符时,如果不采用并发技术,那么程序也只能按顺序一个一个去处理这些文件描述符。

        在本章中,我们将依次介绍select,poll,epoll系统调用,基于简单且重要的原则,本章不会详细介绍select和poll,但他们依然是面试高频考点,希望读者能自行去了解一下。本章将重点介绍epoll,同时介绍epoll中两种重要的触发模式LT水平触发模式和ET边沿触发模式,这些内容是第三章中提到的是I/O处理单元中的核心内容,需要读者理解且记忆。


select系统调用

#include<sys/select.h>
int select(int nfds,fd_set* readfds, fd_set* writefds, 
           fd_set* exceptfds,struct timeval* timeout);
  • 作用:在指定的一段时间内,轮询监听文件描述符上的可读,可写和异常等事件
  • 参数:
    • nfds:指定被监听文件描述符的总数,通常却文件描述符中的最大值+1
    • readfds:可读事件对应的文件描述符的集合
    • writefds:可写事件对应的文件描述符的集合
    • exceptfds:异常事件对应的文件描述符的集合
    • timeout:函数运行的时间
  • 返回值:
    • 成功:就绪文件描述符的总数
    • 失败:-1

        其中fd_set结构指针类型和struct timeval指针类型感兴趣可以自行搜索。

        select系统调用中的文件描述符只有在可读,可写或异常的条件下才被计入就绪文件描述符,而这些具体条件感兴趣的读者自行搜索。


poll系统调用

#include<poll.h>
int poll(struct pollfd* fds,nfds_t nfds,int timeout);
struct pollfd{
    int fd;/*文件描述符*/
    short events;/*注册的事件*/
    short revents;/*实际发生的事件,由内核填充*/
  • 作用:在一定时间内,轮询一定数量的描述符,检测其中是否有就绪事件
  • 参数:
    • fds:pollfd结构类型的数组
      • fd:文件描述符
      • events:fd上有哪些事件(见下图),这些事件通过按位或传入events
      • revents:内核进行修改,通知程序fd上那些事件确实发生了
    • nfds:被监听的事件集合大小
      • nfds_t定义:typedef unsigned long int nfds_t;
    • timeout:poll运行的时间,可以理解为一个倒计时,当时间到达函数返回
      • -1:永远阻塞直到某个事件发生
  • 返回值:
    • 成功:就绪文件描述符的总数
    • 失败:-1


epoll系统调用

        epoll是非常高效的I/O复用函数,它不是一个函数,而是一组函数,即

  1. epoll_create
  2. epoll_ctl
  3. epoll_wait

        epoll把文件描述符上的事件放入一个内核事件表里,而不是像select和poll那样每次调用重复传入文件描述符,但epll需要一个额外的文件描述符来标识这个内核事件表。

epoll_create

#include<sys/epoll.h>
int epoll_create(int size);
  • 作用:创建文件描述符来标识内核事件表
  • 参数:size不起作用,仅仅给内核一个提示,事件表应该有多大
  • 返回值:作为其他epoll函数的第一个参数

epoll_ctl

        创建好了标识内核事件表的文件描述符,即可使用epoll_ctr对内核事件表进行设置

#include<sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
struct epoll_event
{
    _uint32_t events;
    epoll_data_t data;
}
  • 作用:对内核事件表进行操作
  • 参数:
    • epfd:标识内核事件表
    • op:操作类型
      • EPOLL_CTL_ADD:往时间表中注册fd上的事件
      • EPOLL_CTL_MOD:修改fd上的注册事件
      • EPOLL_CTL_DEL:删除fd上的注册事件
    • fd:要操作的文件描述符
    • event:指定事件(结构体内如下)
      • _uint32_t events : 事件类型(与poll的事件类型相同,但要前加“E")
      • epoll_data_t data : 存储用户数据(联合体内如下)
        • void* ptr : 指定与fd相关的用户数据
        • int fd : 事件所从属的目标文件描述符(常用)
        • uint32_t: u32:
        • uint64_t:u64:
        • (PS:联合体union即多选一使用,不能同时使用)
  • 返回值:
    • 成功:0
    • 失败:-1

        事件类型(poll类型的,epoll在poll类型的事件类型前面 + ”E";

epoll_wait

        创建并设置好内核事件表后,即可使用epoll_wait()开始等待事件

#include<sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event* events,int maxevents,int timeout);
  • 作用:在一定时间内接收就绪事件,并加入一个事件数组中等待程序处理
  • 参数:
    • epfd:内核事件表的标识符(epoll_create创建的)
    • events:事件数组,将epfd中的就绪事件取出放进去
    • maxevents:最多监听多少个时间
    • timeout:epoll_wait等待的时间,可以理解为倒计时,时间到了返回
  • 返回值
    • 成功:接收到的事件数量
    • 失败:-1

        epoll_wait函数检测到事件后,会将内核注册表中的就绪事件放入第二个数组中,当工作线程来数组中取事件处理时,就不需要判断事件是否是就绪事件,而poll则需要先判断是否就绪。

        可以这样理解,文件描述符上的事件是一堆未处理的食物,而I/O复用函数作用是将这些食物放入一格一格的容器中。poll容器不管食物是否烹饪过,直接放进去。而epoll会有一个大厨(内核)来将烹饪过的食物放进去。在食客挑选食物食用时(工作线程逻辑处理),如果是poll容器,则需要一个格子一个格子打开判断食物是否可以食用,而epoll容器中都是可以直接食用的食物,食客直接打开食用即可。

LT和ET模式

        epoll对文件描述符的操作又两种模式,LT模式ET模式。

        LT模式(水平触发):LT是默认的工作模式,这种模式epoll相当于一个高效的poll

        ET模式(边沿触发):当往epoll内核事件表中注册一个文件描述符上的EPOLLET事件时,epoll会切换成ET模式

        LT模式:当epoll_wait检测到文件描述符上有事件发生并通知应用程序后,应用程序可以不立即处理事件,当下一次调用epoll_wait时,epoll_wait还会再次向应用程序通告此事件,直到这个事件被处理。

        ET模式:当epoll_wait检测到文件描述符上有事件发生并通知应用程序后,应用程序必须立即处理事件,后续epoll_wait不再通知

        LT模式和ET模式可以这样理解,还是上面那个例子,EPOLL容器安装了一个语音呼叫装置,当容器某一格内有食物(就绪事件)时,它就会呼叫食客来取餐。LT模式下,食客可以不来用餐,这样当主线程循环一遍后又一次调用EPOLL时,它又会再次呼叫食客来取走上次未取走的那个格子里的食物。而ET模式下,EPOLL容器一呼叫,食客就必须来取餐。

EPOLLONESHOT事件

        在使用epoll时可能遇到这样的问题,在某个线程读取完某个socket上的数据开始处理时,这个socket上又来了新的数据,这时另一个线程被唤醒处理这些数据,那么就出现了两个线程处理同一个socket的情况。为了避免这种情况,可以使用EPOLLONESHOT事件。

        一个注册了EPOLLONESHOT事件的文件描述符,操作系统最多可以触发其上的一个事件,且只能触发一次。除非用epoll_ctl函数重置该文件描述符上的EPOLLONESHOT事件。反过来,当一个线程处理完一个socket上的事件,就该立刻重置其上的EPOLLONESHOT事件,保证下次其他线程能处理这个socket。


epoll和select/poll的区别

事件集处理方式

select:使用fd_set。

        fd_set并未将文件描述符和事件绑定,所以需要三个参数来分别传入可读,可写和异常事件。且由于内核对fd_set集合为在线修改,应用程序下次调用select前需要重置三个参数

 poll:使用pollfd

        pollfd内定义文件描述符和事件,所有事件都统一处理;内核每次修改的时revent成员,而event成员不变,所以不用重置事件集参数。

epoll:

        在内核中维护事件表,用独立系统调用epoll_ctr来控制事件表,epoll_wait调用直接从内核时间表中取得注册的事件,无需反复从用户空间读入这些事件。

实现原理和效率

select/poll:

        采用轮询的方式,每次调用都要扫描整个注册文件描述符集合,并将其中就绪的文件描述符返回给用户程序,时间复杂度O(n)

epoll:

        采用回调的方式,内核检测到就绪的文件描述符时触发回调函数,回调函数将文件描述符上的对应事件加入内核就绪队列,内核在合适的时机将就绪事件队列中的拷贝到用户空间。时间复杂度是O(1)

其他区别

工作模式:

        select/poll只有LT模式

        epoll可以使用ET模式

最大文件描述符数:

        select:有最大值,且比poll和epoll少

        poll/epoll:65353

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

低聚糖市场分析:预计2029年将达到26亿美元

低聚糖又名寡糖( oligosaccharide)或少糖类&#xff0c;是一种新型功能性糖源&#xff0c;低聚糖集营养、保健、食疗于一体&#xff0c;广泛应用于食品、保健品、饮料、医药、饲料添加剂等领域。 在我国由于低聚糖行业的技术飞速进步&#xff0c;收率和质量明显提高&#xff0…

React关于类组件ts使用

类组件中定义了两个接口的类型&#xff0c; 分别约束自身数据state对象 与父组件通过props传递进来的数据。 在父组件使用子组件 并传递参数&#xff1b;

guns项目 Failed to register @ServerEndpoint class 问题

问题发生所在 socket-business-websocket-7.2.4.jar tomcat发布测试的时候报Failed to register ServerEndpoint class &#xff0c;查询jar报主要是WebSocketServer 类加载有问题&#xff0c;把jar报中该类注掉&#xff0c;重新实现这个类&#xff0c;删除Component注解问题&…

uibot-native-message-plug5.1.2_0.zip

网上下载的5.1.1 不好用&#xff0c;于是自己找了一个测试没有问题&#xff0c;支持360和chrome 该扩展安装后&#xff0c;使用uibot creator 很容易&#xff0c;不需要敲击代码做爬虫类自动化程序。

当心!recover成为”恶魔“--Go中的容错处理进阶

前言&#xff1a; Go语言本身没有try/catch异常机制&#xff0c;因为Go的三位创始人在设计Go语言之出觉得这样写会变得很繁琐。 但因为&#xff1a;Go本身支持函数多返回值&#xff0c;因此在写函数的时候&#xff0c;可以优先考虑容错处理。 接下来&#xff0c;我们来看看在G…

不小心删了u盘里的word文档怎么恢复(含三种恢复方法)

在日常生活和工作中&#xff0c;U盘因其便携性而成为存储和传输数据的重要工具。然而&#xff0c;有时会发生误删重要文件的情况&#xff0c;如不小心删了U盘里的Word文档。面对这种情况&#xff0c;许多用户感到束手无策。本文将为您介绍如何恢复不小心删除的U盘Word文档&…

Docker从入门到精通

系列文章目录 docker常见用法之镜像构建1 docker 系列文章目录一、镜像的分层结构二、容器的用法三、镜像的构建3.1docker commit 构建新镜像三部曲3.1.1运行容器并且修改容器3.1.2提交容器3.1.2删除docker镜像 3.2Dockerfile构建镜像 系列文章目录一、 Dockerfile写法详解1.1…

基于Mcrosemi M2S090T FPGA 的 imx991 SWIR的SLVS解码(一)

目录 一、平台介绍 二、器件的简介 1、imx991 SWIR Image Sensor 2、M2S090T 三、工程 1、imx991寄存器配置 一、平台介绍 工程开发平台&#xff1a;Libero Version:20231.0.6 Release:v2023.1 文本编辑器&#xff1a;Sublime text3 二、器件的简介 1、imx991 SWIR I…

我自己总结记忆的23种设计模式

1&#xff0c; 对23种设计模式&#xff0c;大家的第一个印象就是抽象繁琐&#xff0c;记不住&#xff01;&#xff01;不常用&#xff1f;&#xff1f; 其实设计模式是非常有用的&#xff0c;大家只要理解设计模式了&#xff0c;思想上就能有质的飞跃&#xff01; 但是&#…

RFID服装物流零售管理系统设计解决方案

一、方案概述 本方案是广东航连科技根据服装企业客户的需求量身定制的解决方案&#xff0c;该方案综合了RFID技术、网络技术、计算机技术、数据库技术和无线通信技术&#xff0c;结合服装企业的实际需求以及航连科技的丰富经验和独特技术&#xff0c;提出了以下基于RFID的物流…

高级 Python 面试问题与解答

文章目录 专栏导读1.什么是PIP&#xff1f;2.什么是 zip 函数&#xff1f;3.Python 中的 __init __ () 是什么&#xff1f;4.Python 中的访问说明符是什么&#xff1f;5.Python 中的单元测试是什么&#xff1f;6.Python全局解释器锁&#xff08;GIL&#xff09;&#xff1f;7.P…

nginx代理七牛云http资源,节省https费用(亲测有效)

七牛云https费用太高了&#xff0c;通过配置服务器https代理到http访问&#xff01; location ~ /qiniu/(.*) { proxy_pass http://qiniu.myweb.cn/$1; proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarde…

Vue3+ElementPlus实例_select选择器(不连续搜索)

1.开发需求 在各大UI框架的select选择器中&#xff0c;在搜索时都是输入连续的搜索内容&#xff0c;比如“app-store”选项&#xff0c;你要输入“app-xxx”&#xff0c;才能匹配这个选择&#xff0c;要是想输入“a-s”这种不连续的匹配方式&#xff0c;就实现不了&#xff0c…

安科瑞应急疏散照明系统在歌舞娱乐等场所的应用

首先必须明确疏散照明并不包含疏散指示标志&#xff0c;疏散照明是为了提供人员疏散时的必要照明&#xff0c;必须达到规定照度&#xff0c;以便逃生时看清逃生的路径&#xff0c;避免出现恐慌及踩踏事故&#xff0c;而疏散指示标志则是提供疏散路径方向引导的&#xff0c;所以…

Python——基本语法(一)

一、程序和⽤户交互 python 中 使⽤ input 函数实现 input("这⾥写提示信息&#xff0c; 必须使⽤引号引起来")二、变量 In [1]: n input("输入数字") 输入数字8In [2]: n Out[2]: 81.变量命名潜规则: 不要以单下划线和双下划线开头&#xff1b;如&…

从零开始的 dbt 入门教程 (dbt core 命令进阶篇)

引 根据第一篇文章的约定&#xff0c;我想通过接下来的几篇文章带大家进一步了解 dbt 的用法&#xff0c;原计划这篇文章我会介绍 dbt 命令的进阶用法&#xff0c;进一步认识 dbt 的配置以及如何创建增量表等等零零散散十几个方面的知识点&#xff0c;结果在我写完命令部分发现…

CMake TcpServer项目 链接静态库/动态库

一、链接静态库 查看项目结构 hehedalinux:~/Linux/LinuxServerCpp-Link$ tree . ├── CMakeLists.txt ├── include │ ├── common │ │ ├── Buffer.h │ │ ├── Channel.h │ │ └── Log.h │ ├── http │ │ ├── HttpRequest…

Android Studio 项目结构

manifests&#xff1a;用于存放安卓程序的配置文件 AndroidManifest.xml&#xff1a;这是Android应用程序的清单文件&#xff0c;包含了应用程序的基本信息和组件声明等java&#xff1a;Java源代码文件存放的根目录 主代码 com.example.app&#xff1a;应用程序的主包名&#x…

nginx重定向,根据端口重定向

//直接跳转 if ($server_port 9058 ) {# return 301 https://$host$request_uri;return 301 http://www.baidu.com; }//重定向&#xff0c;不跳转域名&#xff0c;还是原域名 location / {if ($server_port 9058 ) {proxy_pass http://ts.***.***.com:9059;} }$args:GET请求…

世微AP5160宽电压 LED 降压型恒流芯片14-18V 3A 电源PCB线路

这是一款14-18V 3A 电流的PCB设计方案. 运用的是世微AP5160 电源驱动IC,这是一款效率高&#xff0c;稳定可靠的 LED 灯恒流驱动控制芯片&#xff0c;内置高精度比较器&#xff0c;固定 关断时间控制电路&#xff0c;恒流驱动电路等&#xff0c;特别适合大功率 LED 恒流驱动。 …