【长文梳理webserver核心】核心类篇
前言
有三个核心组件支撑一个reactor实现 [持续] 的 [监听] 一组fd,并根据每个fd上发生的事件 [调用] 相应的处理函数。这三个组件就是 EventLoop 、Channel 以及 Poller 三个类,其中 EventLoop 可以看作是对业务线程的封装,而 Channel 可以看作是对 每个已经建立连接 的封装(即accept(3) 返回的文件描述符)
EventLoop类
先明确一点,作为一个网络服务器,需要有 持续监听、持续获取监听结果、持续处理监听结果对应事件的能力,也就是 循环 去 调用Poller::poll方法获取实际发生事件的Channel集合,然后调用这些Channel里面保管的不同类型事件的处理函数,也就是Channel::HandlerEvent方法。绿色部分不懂没关系,继续看。
总之EventLoop就是负责实现 循环 ,负责驱动循环的主要模块,Channel和Poller都是他的手下,因为EventLoop整合封装了两者,并且向上提供了更方便的接口来使用。
在EventLoop的类定义中,除了⼀些状态量以外,每个 EventLoop 持有⼀个 Poller 的智能指针(对 epoll / poll 的封装),⼀个用于 EventLoop 之间通信的 Channel ,自己的线程 id,互斥锁以及装有等待处理函数的 vector 。很明显, EventLoop 并不直接管理各个连接的 Channel (文件描述符的封装),而是通过Poller 来进行的。 EventLoop 中最核心的函数就是 EventLoop::Loop() 。
EventLoop::loop(),代码如下:
void EventLoop::loop()
{ // 开始事件循环 调⽤该函数的线程必须是该EventLoop所在线程 assert(!is_looping_);assert(is_in_loop_thread());is_looping_ = true;is_stop_ = false;while(!is_stop_){// 1、epoll_wait阻塞 等待就绪事件auto ready_channels = poller_->Poll();is_event_handling_ = true;// 2、处理每个就绪事件(不同channel绑定了不同的callback)for (auto& channel : ready_channels) {channel->HandleEvents();}is_event_handling_ = false;// 3、执⾏正在等待的函数(fd注册到epoll内核事件表)PerformPendingFunctions();// 4、处理超时事件 到期了就从定时器⼩根堆中删除(定时器析构会EpollDel掉fd)poller_->HandleExpire();}is_looping_ = false;
}
每个EventLoop对象都唯一绑定了一个线程,这个线程其实就在一直执行这个函数里面的while循环,这个while循环的大致逻辑比较简单。就是调用Poller::poll方法获取事件监听器上的监听结果。接下来在loop里面就会调用监听结果中每一个Channel的处理函数HandlerEvent( )。每一个Channel的处理函数会根据Channel类中封装的实际发生的事件,执行Channel类中封装的各事件处理函数。比如一个Channel发生了可读事件,可写事件,则这个Channel的HandlerEvent( )就会调用提前注册在这个Channel的可读事件和可写事件处理函数,又比如另一个Channel只发生了可读事件,那么HandlerEvent( )就只会调用提前注册在这个Channel中的可读事件处理函数。
所以总结,每个EventLoop实际上就做了四件事
- epoll_wait阻塞 等待就绪事件(没有注册其他fd时,可以通过event_fd来异步唤醒)
- 处理每个就绪事件
- 执行正在等待的函数(fd注册到epoll内核事件表)
- 处理超时事件,到期了就从定时器小根堆中删除
Channel类
接下来解释EventLoop的两个手下之一Channel,Channel类其实相当于一个文件描述符的保姆。
想要通过 IO 多路复用(epoll / poll)监听某个文件描述符,就需要把这个 fd 和该 fd 感兴趣 的事件通过 epoll_ctl 注册到 IO 多路复用模块(事件监听器)上。当 IO 多路复用模块监听到该 fd 发生了某个事件。事件监听器返回发生事件的 fd 集合(有哪些 fd 发生了事件)以及每个 fd 的事件集合(每个 fd 具体发生了什么事件)。
Channel类则封装了一个 [fd] 和这个 [fd感兴趣事件] 以及事件监听器监听到 [该fd实际发生的事件]。同时Channel类还提供了设置该fd的感兴趣事件,以及将该fd及其感兴趣事件注册到事件监听器或从事件监听器上移除,以及保存了该fd的每种事件对应的处理函数。
Channel类重要的成员变量
- fd_:Channel对象照看的文件描述符
- events_:代表fd感兴趣的事件类型集合,或者说正在监听的事件
- revents_:代表事件监听器实际监听到该fd发生的事件类型集合,或者说是返回的就绪事件,当事件监听器监听到一个fd发生了什么事件,通过Channel::set_revents()函数来设置revents值
- last_events_:上一此事件(主要用于记录如果本次事件和上次事件⼀样 就没必要调用
- read_handler_,write_handler_,update_handler_,error_handler_:这些是std::function类型的各种回调函数,代表这个Channel为这个文件描述符保存的各事件类型发生时的处理函数。
Channel类重要的成员方法
Channel::HandleEvents()方法
/ IO事件的回调函数 EventLoop中调⽤Loop开始事件循环 会调⽤Poll得到就绪事件
// 然后依次调⽤此函数处理就绪事件void Channel::HandleEvents() {events_ = 0;// 触发挂起事件 并且没触发可读事件if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)) {events_ = 0;return;}// 触发错误事件if (revents_ & EPOLLERR) {HandleError();events_ = 0;return;} // 触发可读事件 | ⾼优先级可读 | 对端(客户端)关闭连接if (revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {HandleRead();}// 触发可写事件if (revents_ & EPOLLOUT) {HandleWrite();}//处理更新监听事件(EpollMod)HandleUpdate();
}
每个Channel对象只属于一个EventLoop ,即只属于一个IO线程,只负责一个文件描述符fd的IO时间分发,但并不拥有这个fd,Channel把不同的IO事件分发为不同的回调,回调用C++11的特性function表示。
从Channel类的定义中可以看出,每个Channel持有一个文件描述符,正在监听的事件,已经发生的事件(由Poller返回),以及各个事件的回调函数的Function对象。
总的来说,Channel就是对fd事件的封装,包括注册它的事件以及回调。EventLoop通过调用Channel::handleEvent()来执行Channel的读写事件。Channel::handleEvent() 的实现也非常简单,就是比较已经发生的事件(由 Poller 返回),来调用对应的回调函数(读、写、错误)。
Poller类
Poller 类的作用就是负责监听文件描述符事件是否触发以及返回发生事件的文件描述符以及具体事件。所以一个Poller 对象对应⼀个 IO 多路复用模块。在 muduo 中,⼀个 EventLoop 对应一个Poller 。
Epoll代码如下 :
class Epoll {
public:Epoll();~Epoll();void epoll_add(const sp_Channel& request);void epoll_mod(const sp_Channel& request);void epoll_del(const sp_Channel& request);void poll(std::vector<sp_Channel>& req);
private:int epollFd_;std::vector<epoll_event> events_; // epoll_wait()返回的活动事件都放在这个数组⾥std::unordered_map<int, sp_Channel> channelMap_;
};
Poller类的主要成员变量有三个:
- epollFd_:就是用epoll_create方法返回的epoll句柄
- events:存放epoll_wait()返回的活动事件,是一个结构体
- channelMap_ :这个变量是 std::unordered_map<int, std::shared_ptr<Channel>> 类型,负责记录 文件描述符fd -> Channel 的映射,也帮忙保管所有注册在你这个 Poller 上的 Channel 。
其他函数无非就是对Epoll_ctl(4)和 Epoll_wait(4)的封装
void Epoll::poll(std::vector<sp_Channel>& req) {int event_count = Epoll_wait(epollFd_, &*events_.begin(), events_.size(), EPOLLWAIT_TIME);for(int i = 0; i < event_count; ++i) {int fd = events_[i].data.fd;sp_Channel temp = channelMap_[fd];temp->setRevents(events_[i].events);req.emplace_back(std::move(temp));}// LOG << "Epoll finished";
}
Epoll::poll(1) 这个函数可以说是 Poller 的核心了,当外部调用 poll 方法的时候,该方法底层其实是通过epoll_wait 获取这个事件监听器上发生事件的 fd 及其对应发生的事件,我们知道每个 fd 都是由⼀个Channel封 装的,通过哈希表 channelMap_ 可以根据 fd 找到封装这个 fd 的 Channel 。将 IO 多路复用模块监听到该 fd 发生 的事件写进这个 Channel 中的 revents 成员变量中。然后把这个 Channel 装进 req 中。这样,当外界调用完poll 之后就能拿到 IO 多路复用模块的监听结果( std::vector<sp_Channel>& req )。
相关文章:

【长文梳理webserver核心】核心类篇
前言 有三个核心组件支撑一个reactor实现 [持续] 的 [监听] 一组fd,并根据每个fd上发生的事件 [调用] 相应的处理函数。这三个组件就是 EventLoop 、Channel 以及 Poller 三个类,其中 EventLoop 可以看作是对业务线程的封装,而 Channel 可以看…...

[实用工具]Docker安装nextcloud实现私有云服务和onlyoffice
Nextcloud是一款开源的云存储和协作平台,允许用户在自己的服务器上存储和访问文件,同时提供强大的协作工具。它可以替代商业云存储服务,让用户拥有完全控制和自主管理自己的数据。 Nextcloud支持文件上传和下载,可以通过Web界面、…...
基于STM32设计的生猪健康检测管理系统(NBIOT+OneNet)(240)
文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成1.2 设计思路1.3 项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】项目背景【5】摘要1.4 开发工具的选择【1】设备端开发【2】上位机开发1.5 系统功能总结1.6 系统框架图…...

springboot kafka多数据源,通过配置动态加载发送者和消费者
前言 最近做项目,需要支持kafka多数据源,实际上我们也可以通过代码固定写死多套kafka集群逻辑,但是如果需要不修改代码扩展呢,因为kafka本身不处理额外逻辑,只是起到削峰,和数据的传递,那么就需…...

【华为】基于华为交换机的VLAN配置与不同VLAN间通信实现
划分VLAN(虚拟局域网)主要作用: 一、提高网络安全性 广播域隔离访问控制增强 二、优化网络性能 减少网络拥塞提高网络可管理性 sysytem-view #进入系统视图配置参数 vlan batch 10 20 #批量创建vlan LSW3: int g0/0/1 port…...

力扣题11~20
题11(中等): 思路: 这种题目第一眼就是双循环,但是肯定不行滴,o(n^2)这种肯定超时,很难接受。 所以要另辟蹊径,我们先用俩指针(标志位)在最左端和最右端&am…...

更美观的HTTP性能监测工具:httpstat
reorx/httpstat是一个旨在提供更美观和详细HTTP请求统计信息的cURL命令行工具,它能够帮助开发者和运维人员深入理解HTTP请求的性能和状态。 1. 基本概述 项目地址:https://github.com/reorx/httpstat语言:该工具主要是以Python编写ÿ…...

在2024 VDC,听一曲“蓝心智能”的江河协奏
作为科技从业者,我们每年参加的终端产品发布会和开发者大会,少则几十场。说每一场都别有新意,那自然是不可能的,但每次去vivo的活动现场,总能给我耳目一新的感觉。 雨果说过,音乐可以表达难以用语言描述&am…...
Python编写的数字光刻仿真程序,使用了Hopkins光刻模型和粒子群优化(PSO)算法来优化掩模设计
Python编写的数字光刻仿真程序,使用了Hopkins光刻模型和粒子群优化(PSO)算法来优化掩模设计,以减少光刻过程中的图形偏差。 4. 定义了几个函数来模拟光波通过光刻系统的变化: - `transfer_function`:计算光波的相位变化。 - `light_source_function`:描述光源在各…...
【AD那些事 11】绘制PCB板时“隔离” 的那些事(笔记摘抄)
在设计新板子时发现需要考虑隔离!!!!!!!!!!!于是我在网上找了很多资料,摘抄了一些,整理了一下,作为笔记&#…...

sublime配置(竞赛向)
我也想要有jiangly一样的sublime 先决条件 首先,到官网上下载最新的sublime4,然后在mingw官网上下载最新的mingw64 mingw64官网:左边菜单栏点击dowloads,然后选择MinGW-W64-builds(可能会有点慢)——然后有时候会变成选LLVM-minGW,接着选择…...

双向数据库迁移工具:轻松实现 MySQL 与 SQLite 数据互导
项目概述与作用 该项目的核心是实现 MySQL 和 SQLite 两种数据库之间的数据迁移工具。它能够轻松地将 MySQL 数据库中的数据导出为 SQLite 数据库文件,反过来也可以将 SQLite 数据库中的数据上传到 MySQL 数据库中。这个双向迁移工具非常适用于: 数据库备…...
oracle查询表空间信息
方式一,通过SQLPLUS查看,适用于无PLSQL等工具 sqlplus / as sysdba set line 200 set lines 200 col tablespace_name for a20 col SUM_SPACE(M) for a15 col USED_SPACE(M) for a15 col USED_RATE(%) for a15 col FREE_SPACE(M) for a15 SELEC…...

使用Python编写你的第一个算法交易程序
背景 Background 最近想学习一下量化金融,总算在盈透投资者教育(IBKRCampus)板块找到一篇比较好的算法交易入门教程。我在记录实践过程后,翻译成中文写成此csdn博客,分享给大家。 如果你的英语好可以直接看原文…...
点进HTML初步了解
写在前边 ##关于插件 ①简体中文 ②open-in-browser:自动在浏览器生成html页面; ③Auto Rename Tag:自动匹配标签; ④Live server:实现页面的实时刷新; ##关于快捷键: Ctrl / 用来注释…...

幸运的沈抖,进击的百度智能云
文|白 鸽 编|王一粟 AI对百度智能云的意义,可能远大于任何一家云计算厂商。 2022年5月,分管百度移动生态事业群组(MEG)的集团执行副总裁沈抖,转而担任百度智能云事业群组(ACG&…...
android广播实现PIN码设置
摘要:本文通过广播的方式调用系统设置PIN码的流程实现类似锁机的功能,可供开发人员在联网状态下后台推送消息进行锁机/解锁。有需要的同学可以参考PIN码的流程改为密码等其他形式。 1 定义一个广播接收器 广播action:android.intent.action…...

Mac 需要杀毒软件?
大部分 mac用户普遍认为 Apple mac 不受病毒和恶意软件的影响。这导致许多 Mac 用户误以为无需为 Mac 安装防病毒软件,但事实并非如此。 在这篇文章中,将深入探讨 Mac 安全性的细节,探索针对 Apple 设备的恶意软件类型,并为您…...

Java | Leetcode Java题解之第472题连接词
题目: 题解: class Solution {Trie trie new Trie();public List<String> findAllConcatenatedWordsInADict(String[] words) {List<String> ans new ArrayList<String>();Arrays.sort(words, (a, b) -> a.length() - b.length(…...

CUDA Graphs学习与实验
CUDA Graphs学习与实验 一.参考链接二.测试方案三.测试代码 CUDA图(CUDA Graphs)为CUDA引入了一种全新的工作提交模型。它允许将一系列操作(如内核启动)以图的形式表示,并通过依赖关系将这些操作连接起来。这种图的定义…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...

react更新页面数据,操作页面,双向数据绑定
// 路由不是组件的直接跳转use client,useEffect,useRouter,需3个结合, use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...