【Linux网络编程】谈谈网络编程中的select、poll、epoll、Reactor、Proactor模型(下)

news/2025/2/8 21:11:27 标签: linux, 网络, epoll, IO多路复用, Reactor

在这里插入图片描述

本文目录

  • 一、IO多路复用第二版(epoll
  • 二、epoll三大核心接口
  • 三、异步IO
  • 四、Linux惊群效应与c10K问题
  • 五、主流网络模型介绍
    • 1、基于Thread-based的架构模型
    • 2、Reator模型

epoll_4">一、IO多路复用第二版(epoll

从主动轮询转变为被动通知,一定程度上提升了性能,但是select和poll每次调用都需要拷贝管理的全量的fd到内核态,(每次调用和管理的时候都需要拷贝全量的fd到内核态,然后就绪之后又得拷贝到用户态,最后上层应用判断的时候还要挨个进行判断哪个客户端就绪了),导致影响性能。

所以可以针对拷贝还有模糊通知改进成不拷贝明确通知,这就是两个思路,也就是调用和返回的时候做一个改进。

在这里插入图片描述

epoll_12">二、epoll三大核心接口

epoll_create_14">1、epoll_create()

#include<sys/epoll.h>

int epoll_create(int size)

epoll_create()创建返回的epollfd指向内核中的一个epoll实例,同时该epollfd用来调用所有和epoll相关的接口(epoll_ctl()、epoll_wait())。成功返回时,返回大于0的epollfd,失败时返回-1。需要根据errnum查看错误。

linux2.6.8之后,size参数已经被忽略了,但必须要大于0.

epollfd不再使用需要调用close()关闭,当所有指向epoll的文件描述符关闭之后,内核会摧毁该epoll实例并且释放和其关联的资源。

epoll_ctl_27">2、epoll_ctl()

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epfd:通过 epoll_create() 创建的 epoll 文件描述符(epollfd)。
op:操作类型,可以是以下三种之一:
EPOLL_CTL_ADD:添加一个文件描述符到 epoll 实例中。
EPOLL_CTL_MOD:修改已经添加到 epoll 实例中的文件描述符的监听事件。
EPOLL_CTL_DEL:从 epoll 实例中删除一个文件描述符。
fd:待监听的文件描述符。
event:指向 epoll_event 结构的指针,指定要监听的事件(如读、写、接受连接等)。

一言以蔽之,就是将哪个客户端(fd)的哪些事件(event)交给哪个 epoll(epfd)来管理(op:增删改)。

当一个客户端与server连接之后,就需要把fd添加到epoll中,这个时候对应的就是添加的操作。处理好请求之后,发送数据给客户端,就需要把读事件更新为写事件,这就是更新

在这里插入图片描述

epoll_wait_49">3、epoll_wait()

#include <sys/epoll.h>

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epfd:通过 epoll_create() 创建的 epoll 文件描述符。
events:返回就绪的事件列表,列表中的事件数量通过 epoll_wait() 的返回值传递。
maxevents:最多返回的事件数量,该值用来告诉内核创建的 events 数组有多大。
timeout:超时时间,-1 表示无限期等待,0 表示立即返回,timeout > 0 表示正常超时时间(单位为毫秒)。

返回值cnt:返回就绪的事件数量。0 表示在超时时间内没有就绪的事件列表;大于 0 表示返回就绪列表的个数(后续通过循环遍历 events[0] 到 events[cnt-1]);-1 表示错误,通过 errno 来识别具体错误信息。

epoll_64">4、epoll简单实例

在这里插入图片描述
在这里插入图片描述

epollETLT_68">5、epoll的ET模式和LT模式

在这里插入图片描述

epoll_71">6、epoll内核实现

首先,epoll_create()函数在内核中分配一段空间,并初始化管理监听描述符的数据结构,包括红黑树和就绪事件链表。红黑树用于存储用户空间中感兴趣的文件描述符(fd)及其关联的事件(events),而就绪事件链表则用于存储内核空间中已经准备好进行I/O操作的事件。用户空间通过epoll_create()获得一个文件描述符,该描述符引用了内核空间中的epoll实例。

接下来,epoll_ctl()函数提供了对红黑树的增删改接口,允许上层用户对感兴趣的文件描述符进行操作。具体来说,EPOLL_CTL_ADD用于添加新的文件描述符到红黑树中,EPOLL_CTL_MOD用于更新已经添加的文件描述符的监听事件,而EPOLL_CTL_DEL则用于从红黑树中删除文件描述符。这些操作会修改红黑树中的数据,从而影响后续的事件监听。

然后,epoll_wait()函数用于从就绪事件链表中获取已经准备好进行I/O操作的事件,并将这些事件的文件描述符和关联的事件信息填充到用户空间提供的events数组中,然后返回给上层用户。这个过程是阻塞的,直到有就绪事件可用或者超时。

最后,就绪事件迁移是epoll机制的核心部分。当内核监听到某个文件描述符上有I/O事件发生时,会将该事件从红黑树中迁移一份到就绪事件链表中。这样,epoll_wait()函数就可以从就绪事件链表中获取这些事件,而不需要每次都遍历整个红黑树,从而大大提高了事件处理的效率。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、异步IO

异步I/O是一种I/O操作模式,在这种模式下,应用程序发起I/O请求后可以继续执行,而不需要等待I/O操作完成。当I/O操作完成时,内核会通过某种机制(如信号)通知应用程序。

Linux中的异步IO是io_uring(linux5.1),windows中的异步IO是IOCP。

在这里插入图片描述
那么select/poll/epoll是同步还是异步IO?

本质上都是同步IO,因为在第二阶段(数据的拷贝), 从内核态到用户态数据的一个拷贝的过程,因为这个过程是由用户线程完成的,必须等待数据从内核缓冲区拷贝到用户空间,就算作是阻塞,(如果是由内核线程完成的,就是异步IO)。

也就是说在数据从内核缓冲区拷贝到用户空间的过程中,用户线程是被阻塞的。

尽管select、poll和epoll可以在不阻塞的情况下监视多个文件描述符,但当某个文件描述符准备好进行I/O操作时,用户线程仍然需要执行阻塞的系统调用来实际读取数据。在数据从内核缓冲区拷贝到用户空间的过程中,用户线程是被阻塞的,因此这些模型本质上是同步I/O。

Linux中的aio系列函数(如aio_read)是真正的异步I/O模型。在这些模型中,用户线程发起I/O请求后,立即可以继续执行其他任务,而不需要等待I/O操作完成。数据从内核缓冲区拷贝到用户空间的过程由内核线程异步完成,用户线程不会被阻塞。

在这里插入图片描述

四、Linux惊群效应与c10K问题

Linux惊群效应(Thundering Herd Problem)是指当多个进程或线程同时等待同一个事件时,如果该事件发生,所有等待的进程或线程都会被唤醒,但最终只有一个能够获得资源或锁,对事件进行处理,其余的进程或线程则需要重新进入等待状态。这种现象会导致大量的上下文切换和CPU资源的浪费,从而影响系统性能。惊群效应在服务器的监听等待调用中尤为常见,例如在多个进程中调用accept或epoll_wait等待客户端连接时。

C10K问题关注的是服务器处理大量并发连接的能力。"C"代表客户端(Clients),"10K"指的是大约10000个并发客户端连接。这个问题主要涉及到硬件资源、操作系统限制、应用性能和网络延迟等因素。随着互联网的发展,类似挑战已经扩展至C1M或者C10M。C10K问题不仅涉及到技术层面的挑战,还涉及到如何优化应用性能和调整操作系统参数以适应大规模并发连接的需求。尽管现代硬件和软件的发展已经使得处理大量并发连接成为可能,但随着互联网流量的不断增长,类似的挑战仍然存在。

五、主流网络模型介绍

1、基于Thread-based的架构模型

这个模型也叫做基于线程架构的模型。

在这里插入图片描述

2、Reator模型

Reactor模型是一种事件驱动的设计模式,主要用于处理服务端的多个并发请求。它通过事件分离和事件多路复用机制,实现了高效的事件处理。Reactor模型中定义的三种角色:Reactor负责监听和分配事件,将I/O事件分发给相应的处理器进行处理。其核心思想是将I/O事件的监听和实际的I/O操作分离开来,由事件循环(Event Loop)负责监听I/O事件,当事件发生时,将事件分发给相应的事件处理器(Event Handler)进行处理。

Reactor模型的工作原理可以分为以下几个步骤:事件注册、事件等待、事件分发和事件处理。在事件注册阶段,应用程序将感兴趣的I/O事件(如读、写事件)注册到事件多路分离器中。事件等待阶段,事件循环调用事件多路分离器的等待函数,阻塞等待I/O事件的发生。事件分发阶段,一旦有I/O事件发生,事件循环将事件分发给相应的事件处理器。最后,在事件处理阶段,事件处理器处理具体的I/O事件,如读取数据、处理业务逻辑、发送响应等。

Reactor模型的实现方式有多种,包括单线程Reactor模型、多线程Reactor模型和主从Reactor模型(Master-Slave Reactor)。单线程模型中,所有的I/O事件和业务逻辑都在一个线程中处理,适用于I/O密集型任务,业务处理时间较短的场景。多线程模型中,I/O事件和业务逻辑由不同的线程处理,事件循环线程负责监听I/O事件,并将事件分发给工作线程处理业务逻辑,适用于I/O和业务处理均较密集的场景,能够充分利用多核CPU的优势。主从Reactor模型中,主线程(Master)负责监听I/O事件,并将事件分发给从线程(Slave)处理,从线程池(Slave Pool)中的多个线程负责处理具体的I/O事件和业务逻辑,适用于高并发、大量连接的场景,进一步提升性能和可扩展性。

Reactor_132">Single-Reactor单线程模型

一个epoll对象可以称为一个reactor。

单线程主要针对IO操作而言,也就是IO中的accept、read、write都是在一个线程完成的。

但是这个模型存在一定的问题,目前该模型中,除了IO操作在Reactor线程之外,业务逻辑处理操作也在Reactor线程上,当业务处理逻辑比较耗时,会大大降低IO请求的处理效率。

典型实现就是Redis(4.0之前)。
在这里插入图片描述

随后就演变而来一个Single-Reactor线程池模型。

IO线程处理IO的操作,工作线程处理工作的业务逻辑。

也就是引入线程池,专门处理业务逻辑操作,提升IO响应的速度。但是管理百万连接、高并发大数据的时候,单个Reactor线程还是吃不消,效率会比较低下。

在这里插入图片描述

Reactor_152">Multi-Reactor多线程模型

引入多个Reactor线程,也就是主从结构。Main主要负责接受客户端连接,Main按照一定负载均衡的策略分发给Sub进行处理,Sub负责处理。

后面又延伸了多种拓展模式:单进程(多线程)模式、多进程模式。

多线程典型实现是Netty、Memcached。

多进程模式典型实现就是Nginx。

在这里插入图片描述
在这里插入图片描述


http://www.niftyadmin.cn/n/5845287.html

相关文章

quartus24.1版本子模块因时钟问题无法综合通过,FPGA过OOC问题复盘

因为只负责一个子模块&#xff0c;所以需要单独对该子模块进行综合和过OOC&#xff0c;这时候已经有一些加虚拟pin文件&#xff0c;敲命令让子模块能过OOC的方法。但这个方法的前提是先过综合&#xff0c;然后再敲命令让虚拟管脚命令成功&#xff0c;最终可以过OOC。 今天负责…

【电商系统架构的深度剖析与技术选型】

以下是对电商系统架构的深度剖析与技术选型&#xff1a; 一、电商系统架构剖析 整体架构 前台系统&#xff1a;是用户直接交互的部分&#xff0c;包括用户界面、商品展示、购物车、订单结算等模块。需注重用户体验&#xff0c;确保页面设计美观、商品信息清晰、购物流程简便。…

荣耀内置的远程控制怎样用?荣耀如何远程控制其他品牌的手机?

荣耀手机没有内置的远程控制功能&#xff0c;倒是有一项内置的【远程守护】功能&#xff0c;可以共享定位。如果家里的老人、小孩都使用荣耀手机&#xff0c;那么可以共享定位&#xff0c;随时知道人在哪&#xff0c;避免走丢。 荣耀手机【远程守护】功能的使用步骤&#xff1a…

A2DP/HFP音频蓝牙模块+玩具,开启儿童成长智能时代

音频蓝牙模组在玩具中的应用非常常见&#xff0c;特别是在需要音频互动、音乐播放或语音功能的玩具中。 一、应用场景 1. 音乐播放玩具&#xff1a;玩具通过蓝牙连接主设备(如手机或平板)&#xff0c;播放存储在主设备中的音乐或音效。 2. 语音互动玩具&#xff1a;玩具可以…

node.js的require()

2009年&#xff0c;Node.js 项目诞生&#xff0c;所有模块一律为 CommonJS 格式。 时至今日&#xff0c;Node.js 的模块仓库 npmjs.com &#xff0c;已经存放了15万个模块&#xff0c;其中绝大部分都是 CommonJS 格式。 这种格式的核心就是 require 语句&#xff0c;模块通过…

FPGA实现SDI视频缩放转UltraScale GTH光口传输,基于GS2971+Aurora 8b/10b编解码架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案本博已有的 SDI 编解码方案我这里已有的FPGA图像缩放方案 3、工程详细设计方案工程设计原理框图SDI 输入设备GS2971芯片BT1120转RGB…

【SQLite】设置本地时间戳默认值

使用CURRENT_TIMESTAMP创建&#xff0c;发现在部分运行环境时间不符&#xff0c;考虑用datetime(now, localtime)创建语句 CREATE TABLE logs (id INTEGER PRIMARY KEY,event TEXT,created_at TEXT DEFAULT CURRENT_TIMESTAMP -- 或 datetime(now, localtime) );在 SQLite 中&…

【C语言】传值调用与传址调用详解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;传值调用1. 什么是传值调用&#xff1f;2. 示例代码&#xff1a;传值调用失败的情况执行结果&#xff1a; 3. 为什么传值调用无法修改外部变量&#xff1f; &#x1f4…