操作系统常问问题
1️⃣ 什么是虚拟地址?操作系统 为每个进程都分配了独立的 虚拟地址。并且,操作系统提供一种机制,将不同进程的虚拟地址和不同的物理内存地址映射起来。如果程序要访问虚拟地址,就由操作系统转换成不同的物理内存地址。 之后,进程持有的虚拟内存地址会通过 CPU 芯片中的 MMU 的映射关系,将其转换成物理内存地址。 2️⃣ 操作系统是如何管理虚拟地址与物理地址之间的关系?主要有两种方式,分别是 内存分段 和 内存分页 3️⃣ 分段机制下,虚拟地址和物理地址是如何映射的?分段机制下的虚拟地址由两部分组成,段选择因子 和 段内偏移量。 段选择因子 最重要的就是 段号,用作 段表 的索引。其中 段表 保存了 段的基地址 以及 段的界限地址。通过 段基地址 加上 段内偏移量 得到 物理内存地址。 4️⃣ 分页机制下,虚拟地址和物理地址是如何映射的?分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。 这样一个连续并且尺寸固定的内存空间,叫做页 Page。 分页机制下的虚拟地址分为两部分,页号 和 页内偏移。页号 作为 页表 的索引,页表 包含物理页在物理内存的 基地址,这个 基地址 加上...
了解 Linux 防火墙
1️⃣ Netfilter提到 Linux 防火墙,很多人会先想到 iptables、nftables、firewalld、ufw,它们本质上都只是 用户态 的 配置工具。 真正负责 “拦包、改包、放行 /丢弃” 的核心,是 Linux 内核里的 Netfilter。 从上图可以看出:流量流动 是先从网卡,再到 linux 内核,最后抵达到网络应用。 流量过滤 指的是先让流量从网卡进来,然后在 linux 内核里进行过滤处理,最后进入网络应用。这样网络应用前面就有一道防火墙,防火墙就位于 linux 内核的实现中。 挂载点Netfilter 向 内核协议栈 中 不同的位置 注册 钩子函数 HOOK 来对数据包进行过滤或者修改操作,这些位置称为 挂载点。 Netlfilter 通常有 5 个 HOOK 点: PREROUTING:数据包进入路由表之前 INPUT:通过路由表后目的地为本机 FORWARD:通过路由表后,目的地不为本机 OUTPUT:由本机产生,向外发送 POSTROUTIONG:发送到网卡接口之前。 2️⃣ iptablesiptables...
多态及其虚函数底层原理
1️⃣ 什么是多态?众所周知,多态 是面向对象三大特性的核心。很多朋友停留在 “加个 virtual 关键字就能实现多态” 的表层认知。 多态的核心在于:同一个接口,不同的实现。举个简单的例子: 假设有一台通用机器人(基类),它能干很多事,比如打扫、修理等。咱们根据实际需要,造出了清洁机器人(派生类)和修理机器人(另一个派生类),它们具体干的活就不一样了。 那么,在 C++ 实现多态的底层原理是什么呢?这一切的答案就在于 虚函数表。接下来,将会从以下几个方面进行讲解: C++ 的绑定方式 虚函数表 vtable 的结构 虚指针 vptr 与 vtable 的关系以及内存布局 虚函数的实现流程 面试常问问题 2️⃣ C++ 的绑定方式在 C++ 中,“绑定” 通常是将 “函数调用” 和 具体的 “函数实现” 关联起来。这个过程可能发生在 编译期,也可能发生在 运行期,因此,C++ 中的绑定方式分为 静态绑定 和 动态绑定。 静态绑定静态绑定 又称 早绑定,是 C++ 中默认的绑定方式。 发生时机: 在 编译阶段 由编译器完成。 核心逻辑:...
智能指针
1️⃣ 背景在 C++ 中,动态内存的管理通过运算符 new/ delete 来完成的: new:在动态内存中为对象分配空间并返回一个指向该对象的指针。 delete:接收一个动态对象的指针,销毁该对象,并释放与之相关的内存。 动态内存的使用很容易出现问题,因为确保在正确的时间释放内存是极其困难的 !!! 忘记释放内存:内存泄露 释放一块仍有指针引用的内存:产生引用非法内存的指针 为了更安全地使用动态内存,新标准库提供了两种智能指针来管理动态内存: 智能指针的行为类似于常规指针,区别在于它自动释放所指向的对象 shared_ptr:允许多个指针指向同一个对象。 unique_ptr:只允许单个指针独占一个对象。 2️⃣ shared_ptr 类概念std::shared_ptr<T> 是一个类模板,采用 RAII 手法,它的对象行为像指针,但是它可以记录有多少个对象共享它所管理的内存对象。 使用经常通过如下两种方式创建 std::shared_ptr 对象: auto p = std::shared_ptr<T>(new...
HttpServer 模块
1️⃣ 概述在 sylar 框架中,HttpServer 是 TcpServer 的核心派生类,也是整个框架在 Web 开发领域的直接入口。如果说 TcpServer 负责的是 底层 的 “连接管理”,那么 HttpServer 负责的就是 “业务协议生命周期的全链路治理”。 HttpServer 不仅继承了 TcpServer 高并发、协程驱动 的优良基因,还引入了现代 Web 框架必备的 SSL 安全通信、中间件插件化、**Session 状态管理** 以及 Servlet 路由分发 等高级特性。 2️⃣ 设计核心HttpServer 的 核心逻辑 高度凝练在重写的 handleClient 方法中。 handleClient 将 socket 接收到的 Http 请求 转化成最终的 HTTP 响应,这其中经历了一套标准化的 “流水线” 操作: 安全加固 (SSL/TLS): 识别连接属性。若是 HTTPS 请求,则在最前端完成 非对称加密握手 逻辑(doHandShake),确保后续数据在加密流(SslSocketStream)中传输。 状态化封装...
TcpServer 模块
1️⃣ 概述在高性能网络框架中,TcpServer 模块往往处于 “承上启下” 的核心地位: 向下 封装了底层的 Socket 套接字细节, 向上 为各类 应用层协议 提供基础的连接管理和数据传输能力。 在 sylar 框架中,TcpServer 是一个高层次的封装,旨在提供一个 简单、稳定 且 支持多核并发 的 TCP 服务器基类。TcpServer 深度集成了 协程调度器 (IOManager) 和 套接字封装 (Socket),使得开发者可以专注于业务逻辑,而无需处理复杂的非阻塞 I/O 和多线程竞争。 2️⃣ 设计理念在 sylar 框架中,TcpServer 不仅仅是一个简单的套接字监听器,它是底层网络组件与复杂业务协议之间的核心桥梁。其架构设计的核心灵魂在于:通过高度解耦实现极致的并发效率与扩展性。 基于 Reactor 模式的双调度器架构这是 TcpServer 高并发能力的核心。它将网络事件处理划分为两个阶段,并允许通过不同的 IOManager 进行物理隔离: Accept 调度器:...
HOOK 模块
1️⃣ 什么是 HOOKHOOK ,中文简称:钩子 ,是对 函数/系统调用 API 进行一次封装。主要是将其封装成一个与原始的 函数/系统调用 API 同名的接口。 调用这个接口的时候,会 先进行封装的操作,再执行 原始 的 函数/系统调用API sylar 中的 HOOK,就是为了在不重写代码的情况下,把原有代码中的 同步 socket api 都转换为 异步 操作,以提升性能。 还可以用 C++ 的子类重载来理解 HOOK。在 C++ 中,子类在重载父类的同名方法时,一种常见的实现方式是子类先完成自己的操作,再调用父类的操作,如下: 12345678910111213141516class Base {public: void Print() { cout << "This is Base" << endl; }}; class Child : public Base {public: ///...
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️⃣...
