面经 wxg_0312
面经 进程和线程的区别?(进程是资源分配的基本单位,线程是 CPU 调度的基本单位) 进程和线程的开销有没有差异?(进程切换时要切换更完整的上下文,线程切换通常更轻量) 协程了解过嘛?(协程是用户态线程,是程序员自己进行切换) 在应用程序中读文件涉及进程的切换嘛?(读文件一定会发生 用户态到内核态的切换;是否发生进程/线程切换,要看会不会 阻塞) 为什么要切换进程?(CPU 是并发处理操作系统的任务的,当前进程阻塞,就需要让出 CPU 去做其他事情) 为什么不在当前进程内直接读而是切换到其他进程?(为了提高 CPU 利用率) 什么 IO 会阻塞当前线程?(同步阻塞 IO) 访问磁盘的时是直接访问到硬件还是用过一些系统的转换?(DMA 请求过程) 虚拟内存的概念?(虚拟内存 就是操作系统给每个进程看到的一套 “虚拟的、连续的内存空间” ) 如果把换页交给应用进程可以嘛? 理论上应用进程可以参与内存管理策略,但不能把换页完全交给应用进程。 因为 换页 需要了解整个系统的内存使用情况,还涉及 页表修改、物理页分配 和 缺页异常处理,这些都属于 内核权限。 内存管理...
STL 常问问题
序列式容器1️⃣ C++ 容器有哪些? 序列式容器: vector、list 关联式容器: map、unoredred_map 2️⃣ vector 超出容量会怎样? vector 会先申请一块 1.5/2 倍 的当前内存大小的连续内存; 把原来元素 拷贝/移动 到新内存; 释放旧内存,再把新元素插进去 3️⃣ vector 扩容后会带来什么后果? 因为元素移动到了新内存,所以原来元素的地址可能全变了。 凡是指向原来 vector 内部元素的 迭代器、指针、引用失效 4️⃣ vector 如果申请新内存失败会怎样 可能抛出异常,一般是 std::bad_alloc 5️⃣ size() 和 capacity() 的区别? size(): 当前已经有多少个元素 capacity(): 当前底层内存最多还能容纳多少个元素而不触发扩容 6️⃣ resize() 和 reserve() 的区别? reserve(): 让 vector 的内存容量至少达到 n,避免后续频繁扩容。 n <= capacity():通常什么都不做; n >...
协程服务器常问问题
1️⃣ 面经一 (腾讯) 什么是协程(用户态线程,用户栈,用户态切换) 协程和线程相比有什么优势(轻量级、切换快、开销小,用户管理,配合非阻塞 IO 更好地实现异步并发) 更好地实现异步并发是怎么理解的(IO 操作时检测到需要等待缓冲时切换协程) 线程也可以在等待时切换,协程的优势是什么(用户自己操作) 轻量级怎么理解(线程的栈是 MB 级别的,协程的栈是 KB 级别的,线程在内核中切换,协程在用户态中切换) 怎么设置非阻塞(fctnl) 返回EAGAIN是什么意思(缓冲区没准备好) 互斥锁怎么实现的(访问锁时如果锁被占用就阻塞,加入阻塞队列,锁被释放时唤醒阻塞队列的中的线程进入就绪队列) http协议怎么解析的(引入了其他项目的解析器) http 是什么格式(消息行 消息头 消息体) N:M 的协程调度器是什么(N线程处理M协程) 如何调度(先来先服务,线程依次去协程队列取任务执行,如果没有任务就执行空闲协程,陷入 epoll_wait,有任务时再唤醒空闲线程去执行任务) 如果所有线程都不空闲,其他协程是不是就不调度了...
插件常问问题
1️⃣ 项目难点 流量线程 会根据 虚拟地址 高频查询客户端信息,但客户端断连时 pcc 会从 在线上下文 里移除。如果流量线程直接依赖 pccMap,就会面临 pcc 生命周期不稳定的问题。 所以我没有让 流量线程 直接访问 pcc,而是专门维护了一个 virtual_address_map,把流量线程需要的字段提前从 pcc 中抽出来,按 virtual_address -> Json 的形式保存。这样流量线程只读 轻量级映射,不依赖 pcc 本身,从而规避了断连场景下访问失效上下文的问题,同时也减少了重复组装 JSON 的开销。 2️⃣ 项目优化请求链路优化 根据场景不同,将 证书校验、用户规则下载、连接状态上报 进行拆分: 证书校验: 走 **同步 fast_fail**(服务器不可访问则立即返回),因为证书校验在主链路上,必须立即返回; 用户访问规则下载: 走 **异步 gRPC**,基于用户的 完成队列 (Completion Queue) 提前下载用户规则,同时避免阻塞 OpenVPN...
IO 多路复用
1️⃣ 同步阻塞 IO造成的问题 两处阻塞: 1. 进程刚调用 recv ; 2. 等待内核将 数据 拷贝到 用户缓冲区 两处进程切换: 1. 连接持续没有数据到达; 2. 网卡将数据从 内核 复制到 socket 的 等待队列,进程会被唤醒 单对单连接:一个进程只能等待一条连接 2️⃣ 同步非阻塞 IO优点将数据从 网卡 拷贝到 socket 的内核空间 这一阶段变成 非阻塞模式。 用户进程调用 recvfrom() 会重复发出请求,检查数据是否到达 socket 的 等待队列。如果没有到,会立即返回。 缺点没有解决:当数据到达 socket 的 等待队列,用户进程需要将数据从 内核空间 拷贝到 用户空间。 3️⃣ 同步阻塞 IO 的数据接收流程 服务端使用系统调用 socket 会陷入到内核态,内核就会创建 socket 内核对象,其主要包含两个重要结构体: (进程)等待队列: 存放了进程的 进程描述符 和 回调函数。 (数据)接收队列: 存放了网卡接收到的需要该 socket 的数据 服务端当通过 socket 调用 recv 函数时,会执行...
零拷贝
1️⃣ 传统 I/O 过程在没有 DMA 技术之前,I/O 过程如下所述 (以 read 举例): 用户执行系统调用 read:将控制权从 用户态 转换到 内核态,此时进程阻塞在这里。 CPU 向磁盘发送 I/O 请求:磁盘接收到 I/O 请求后,就需要的数据填充到自己的磁盘缓冲区,然后产生中断信号。 磁盘向 CPU 发送 I/O 中断信号: CPU 先就把磁盘缓冲区的数据一次一个字节的读进到自己的 寄存器。 再把 寄存器的数据 写入到 内存 中,但是传输过程中 CPU 无法执行其他任务。 系统调用 read 返回:将控制权从 内核态 转换到 用户态。 2️⃣ DMA 技术定义: 在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。 用户执行系统调用 read:将控制权从 用户态 转换到 内核态,此时进程还是会阻塞在这里。 CPU 向 DMA 发送 I/O 请求:此时 CPU 就可以执行其他任务,搬运工作就交给 DMA。 DMA...
进程虚拟内存管理
1️⃣ 进程的虚拟地址空间 2️⃣ 内核如何管理进程?内核通过 task_strcut 来对进程进行管理 因为在进程描述符 task_struct 结构中,有一个专门描述 进程虚拟地址空间 的内存描述符 mm_struct。由于每个进程都有唯一的 mm_struct 结构体,因此每个进程的虚拟地址空间都是 独立的。 12345678910struct task_struct { // 进程id pid_t pid; // 用于标识线程所属的进程 pid pid_t tgid; // 用于标识线程所属的进程 pid pid_t tgid; // 内存描述符表示进程虚拟地址空间 struct mm_struct *mm;}; 子进程是如何被创建的? 调用 fork() 函数创建进程的时候, 子进程地址空间 的 mm_struct 结构会随着子进程描述符 task_struct 的创建而创建。 copy_process 函数中创建 task_struct...
面经 wxg_0307
1️⃣ Linux 是怎么实现并发的?有了进程和线程就可以实现并发了吗?第一层:任务抽象Linux 把 程序执行流 抽象成 “任务”,常见表现为: 进程: 资源分配的基本单位,拥有独立地址空间 线程: CPU 调度的基本单位,同一进程内线程共享地址空间、文件描述符等资源 第二层:调度器Linux 通过 调度器 把 CPU 时间切给不同任务。 即使只有一个 CPU 核,也可以通过 时间片轮转 + 上下文切换,让多个任务 “看起来同时在运行”,这叫 并发。 第三层:中断与系统调用用户程序一旦发生: 时钟中断 缺页中断 系统调用CPU 会陷入内核,内核就有机会切换任务等。 第四层:阻塞/非阻塞 + 多路复用2️⃣ 单核 CPU 可以实现多线程吗? 单核 CPU 上只能做到:多线程并发,不能做到真正的并行。 3️⃣ 虚拟地址 虚拟地址 是程序看到的地址,不是物理内存条上的真实地址。 4️⃣ 程序的地址空间 .text 段 .rodata 段 .data 段 .bss 段 堆 heap 内存映射区 栈...
操作系统常问问题
虚拟内存1️⃣ 什么是虚拟地址?操作系统 为每个进程都分配了独立的 虚拟地址。并且,操作系统提供一种机制,将不同进程的虚拟地址和不同的物理内存地址映射起来。如果程序要访问虚拟地址,就由操作系统转换成不同的物理内存地址。 之后,进程持有的虚拟内存地址会通过 CPU 芯片中的 MMU 的映射关系,将其转换成物理内存地址。 作用: 虚拟内存可以使得进程对运行内存超过物理内存大小: 因为程序运行符合局部性原理,CPU 访问内存会有很明显的重复访问的倾向性 对于那些没有被经常使用到的内存,我们可以把它换出到物理内存之外,比如硬盘上的 swap 区域。 由于每个进程都有自己的页表,所以每个进程的虚拟内存空间就是相互独立的 页表里的页表项中除了物理地址之外,还有对页的标志位 2️⃣ 操作系统是如何管理虚拟地址与物理地址之间的关系?主要有两种方式,分别是 内存分段 和 内存分页 3️⃣ 分段机制下,虚拟地址和物理地址是如何映射的?分段机制下的虚拟地址由两部分组成,段选择因子 和 段内偏移量。 段选择因子 最重要的就是 段号,用作 段表 的索引。其中 段表 保存了 段的基地址 以及...
数据库常问问题
1️⃣ 索引1️⃣ 什么是索引? 索引的定义就是 帮助存储引擎快速获取数据的一种数据结构 2️⃣ 索引的种类我们可以按照 四个角度 来分类索引。 按 「数据结构」 分类:B+tree 索引、Hash 索引、Full-text 索引。 按 「物理存储」 分类:聚簇索引(主键索引)、二级索引(辅助索引)。 按 「字段特性」 分类:主键索引、唯一索引、普通索引、前缀索引。 按 「字段个数」 分类:单列索引、联合索引。 3️⃣ 联合索引的最左匹配原则 按照 最左优先 的方式进行索引的匹配。如果不遵循「最左匹配原则」,联合索引会失效。 4️⃣ 索引下推 针对联合索引(a, b),在执行 select * from table where a > 1 and b = 2 语句的时候,只有 a 字段能用到索引。 那在联合索引的 B+Tree 已经找到第一个满足条件的主键值(ID 为 2)后,还需要判断其他条件是否满足(看 b 是否等于 2),那是在 联合索引里判断?还是回 主键索引去判断...
