IOManager 模块
1️⃣ 什么是 IOManager 模块IOManager 模块可以理解为 Sylar 框架里的“协程版 I/O 事件循环 + 调度中枢”。它把 Linux 的 epoll 事件机制和协程调度器 Scheduler 融合在了一起。 当某个文件描述符 socket、pipe 等变得可读/可写时,IOManager 不会像传统 Reactor 那样直接在 epoll 线程里执行回调,而是把对应的回调函数或协程 Fiber 重新投递到调度器队列中,由线程池里的工作线程去执行。这样一来,业务逻辑可以用“同步写法”的协程风格组织,却能获得“异步 I/O”的高并发能力。 更具体地说,IOManager 维护了一个 FdContext 表,用来记录每个 fd 关注的读/写事件以及事件触发后要执行的回调函数或等待中的协程。它内部的 idle() 协程长期阻塞在 epoll_wait 上 一旦事件到来,就通过 triggerEvent() 将任务调度出去;同时它还继承 TimerManager,把定时器超时回调也纳入同一套调度体系,实现“IO 事件 +...
内存对齐
1️⃣ 什么是内存对齐定义内存对齐(Memory Alignment) 通常指的是: 数据在内存中的起始地址要满足某种“倍数关系”。 比如一个 int 通常是 4 字节,如果它要求 4 字节对齐,那么它的起始地址往往需要是 4 的倍数 double 常见要求 8 字节对齐,则起始地址需要是 8 的倍数。 1234567891011//32位系统#include<stdio.h>struct { int x; char y;}s;int main() { printf("%d\n",sizeof(s)); return 0;} 此时的输出结果是 8B,而不是想象中的 5B = 4B + 1B。 原因 现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始 但是实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数 k(通常它为4 或 8)的倍数,这就是所谓的内存对齐。 在...
定时器模块
1️⃣ 什么是定时器定时器( Timer )本质上就是一种“按时间触发任务”的机制:你把一个任务 task 交给它,并告诉它“多久以后执行”或者“每隔多久执行一次”,定时器负责在合适的时间点把这个任务触发出来。 你可以把 Timer 理解成一个“闹钟”,而 TimerManager 就是“闹钟管理器”:你只负责设定时间和要做的事,到点后由它统一唤醒并执行对应任务。 在服务器/网络框架(比如 Sylar )里,定时器通常用来做这些事: 超时控制:连接超时、读写超时、RPC 超时重试 周期任务:心跳包、定期统计、定期清理缓存/过期会话 延迟执行:延迟关闭连接、延迟回收资源、延迟投递任务 事件循环协作:让 epoll/select 等 I/O 等待不会“傻等”,而是能在最近的定时点醒来。具体来说,事件循环会通过 getNextTimer() 计算“距离最近一次定时任务还要等多久”,并把这个时间作为 epoll_wait 的 timeout,这样既能等待 I/O 事件,又不会错过定时器到期触发。 2️⃣ 整体框架 3️⃣...
布尔类型的大小
1️⃣ 布尔类型的大小在 C/C++ 里 bool 通常占 1 个字节,也就是 sizeof(bool) == 1 在绝大多数编译器/平台上都成立。 123456#include <iostream>int main() { std::cout << sizeof(bool) << "\n";}// 输出结果:1 2️⃣ 为什么是一个字节 byte 而不是一个比特 bit 内存通常按字节寻址 在计算机系统中,一个地址通常指向一个字节(byte) 如果一个对象要有自己的地址(比如 &b),那它至少得占一个可寻址的单位——通常就是 1 字节。 实现 bool 用 1 字节最简单、最快 CPU 读写一个字节使用后 load/store 一条指令就够了。 参考资料: 为什么布尔占用一个字节(byte),而不是一位(bit)
协程调度模块
1️⃣ 什么是协程调度器?协程调度器(Scheduler)本质上是把“要运行的协程/任务”分配到“可用的线程”上执行的调度中心。在 Sylar 里它封装的是一种 N-M 调度模型:N 个协程(Fiber / 回调函数)复用 M 个线程。调度器内部维护一个线程池和任务队列,外部通过 schedule() 把 Fiber 或 std::function<void()> 投递进来;当队列从空变为非空时,调度器会通过 tickle() 唤醒处于 idle() 的空闲线程/空闲协程去取任务执行。这样做的好处是:协程切换发生在用户态、成本低,同时又能利用多线程把不同协程并行跑起来,实现高并发下的高效调度与资源利用。 2️⃣ 整体框架图 3️⃣ 代码类图 4️⃣...
指针与引用
1️⃣ 指针什么是指针?指针: 指向另外一种类型的复合类型。 指针的大小及用法?在 64 位计算机中,指针占 8 个字节空间。 1234567891011#include<iostream>using namespace std;int main(){ int *p = nullptr; cout<<sizeof(p)<<endl; // 8 char *p1= nullptr; cout<<sizeof(p1)<<endl; // 8 return 0;} 指针的用法 指向普通对象的指针 常量指针 函数指针 指向对象成员的指针,包括指向对象成员函数的指针和指向对象成员变量的指针。 this 指针:指向类的当前对象的指针常量。 什么是野指针和悬空指针? 悬空指针 若指针指向一块内存空间 当这块内存空间被释放后,该指针依然指向这块内存空间 此时,称该指针为”悬空指针”。 123void *p = malloc(size);free(p); // 此时,p...
协程模块
1️⃣ 什么是协程?协程是一种比线程更轻量级的用户态并发单位,可以在一个线程内部并发执行多个任务,并且支持在任务之间进行主动挂起与恢复,从而实现看起来像并行实际上是串行的异步逻辑。 2️⃣ ucontext在 sylar 框架中,协程 Fiber 是基于 Linux 提供的 <ucontext.h> 实现的,该头文件中的 API 提供了保存、切换、恢复程序执行上下文的能力,允许用户在用户态实现轻量级的协程调度机制。 概述ucontext 是 POSIX 标准中定义的一组函数,用于实现用户级上下文切换。它允许程序保存和恢复执行上下文(如寄存器、程序计数器、栈等),常用于实现协程、轻量级线程或任务调度等功能。以下是对 ucontext 的详细介绍,并结合代码进行说明。 ucontext 是实现协程的一种方式。因为协程本质上就是一种用户级的线程, 既然是一种特殊的线程, 其上下文切换的实现方式应该和线程的上下文切换类似, 也需要保存必要的寄存器、程序计数器、栈等信息。 核心功能ucontext...
线程模块
线程模块线程的作用 在现代 C++ 网络服务器框架中,线程模块是实现并发处理、任务调度和系统资源充分利用的核心组件。Sylar 框架中的线程模块(Thread 类)封装了 POSIX 的 pthread 接口,简化了线程的创建、命名、同步与生命周期管理。 整体框架 模块解析私有成员变量 m_name :当前子线程的名字,在构造函数中我们需要给每一个线程赋予名字。 m_id :当前子线程的真实内核级线程ID,Linux 下使用 pid_t 唯一标识一个线程。 m_thread :当前子线程的线程标识符,用于表示一个线程对象。 m_cb :当前子线程绑定的回调函数,同样在构造函数中需要给每一个线程赋予它们所需要执行的函数 m_semphore :当前子线程的信号量,用于进行子线程和主线程之间的同步。 重要成员函数 构造函数 12345678910111213141516Thread::Thread(std::function<void()> cb, const std::string &name) : m_cb(cb) ,...
配置模块
配置模块配置模块的作用 统一管理配置项:通过集中式管理配置项,避免硬编码,便于维护与管理。 支持多种类型配置:包括基础类型(如 int, string)、容器类型(如 vector, map)、以及自定义类型(如 CorsConfig)。 支持 YAML 配置文件解析:便于配置文件的可读性和层级表达。 支持运行时动态更新:变更配置项时触发回调函数,便于系统感知配置变更。 配置项基类 整体架构 解析 ConfigVarBase 是所有配置的基类,它主要有两个成员变量 m_name 和 m_description 。前者便是每个配置都应该有的名称,后者表示对该配置的描述 ConfigVarBase 主要包含三个虚函数:toString(),fromString(),getTypeName(),用于给配置子类进行继承。前两个虚函数 toString()和fromString()主要用于配置子类的序列化和反序列化,而后者的 getTypeName() 用于返回配置子类的类型。 类型转换模板类 lexicalCast...
日志模块
日志模块1️⃣ 日志的作用 在服务器的运行过程中,日志可以记录服务器运行过程中的各种事件,所以日志是非常有作用的。 记录服务器的启动和关闭 记录客户端连接/断开 记录HTTP 请求的接收与响应 记录协程调度执行情况 记录异常或错误信息(如断网、文件打不开等) 当运行过程中当出现异常行为(比如响应延迟、崩溃、死锁等)时,开发者可以通过日志快速定位问题所在: 快速定位问题发生的代码逻辑 分析错误发生的上下文(线程 ID、时间戳、调用路径等) 重现问题流程 在 sylar 服务器框架中,使用了类似 Log4cpp 的结构,支持多级日志输出,支持流式日志风格写日志和格式化风格写日志,支持日志格式自定义,日志级别,多日志分离等等功能 2️⃣ 日志级别 整体框架 sylar 框架中主要使用 logLevel 表示日志级别 日志等级 日志等级主要根据严重程度,从大到小依次排序123456789101112enum Level { FATAL = 0, // 致命情况,系统不可用 ALERT = 100, //...
