1️⃣ 面经一

  • 什么是协程(用户态线程,用户栈,用户态切换)
  • 协程和线程相比有什么优势(轻量级、切换快、开销小,用户管理,配合非阻塞 IO 更好地实现异步并发)
  • 更好地实现异步并发是怎么理解的(IO 操作时检测到需要等待缓冲时切换协程)
  • 线程也可以在等待时切换,协程的优势是什么(用户自己操作)
  • 轻量级怎么理解(线程的栈是 MB 级别的,协程的栈是 KB 级别的,线程在内核中切换,协程在用户态中切换)
  • 怎么设置非阻塞(fctnl
  • 返回EAGAIN是什么意思(缓冲区没准备好)
  • 互斥锁怎么实现的(访问锁时如果锁被占用就阻塞,加入阻塞队列,锁被释放时唤醒阻塞队列的中的线程进入就绪队列)
  • http协议怎么解析的(引入了其他项目的解析器)
  • http 是什么格式(消息行 消息头 消息体)
  • N:M 的协程调度器是什么(N线程处理M协程)
  • 如何调度(先来先服务,线程依次去协程队列取任务执行,如果没有任务就执行空闲协程,陷入epoll_wait,有任务时再唤醒空闲线程去执行任务)
  • 如果所有线程都不空闲,其他协程是不是就不调度了 (不会立刻调度,要等当前占着线程的协程让出执行权)
  • 其他协程就不调度了吗
    • 当前框架是协作式 / 非抢占式调度。调度器只能在协程主动让出时切换,不能强行把正在运行的协程“抢下来”。
    • 当前框架更适合 IO 密集型,不适合 CPU 密集型
    • Go 在这方面做得更完善。Go 从 1.14 开始支持 goroutine 的异步抢占,但那需要安全点、信号和运行时配合,复杂度比我当前项目高很多
  • 堆排序时间复杂度(O(nlogn),建堆后 n 次下滤)
  • 快速排序时间复杂度(平均 O(nlogn),最坏 O(n2)

2️⃣ 面经二

  • 面向对象的三大特性(封装、继承、多态)
  • new/malloc 的区别(性质、原理、大小、失败)
  • 虚函数表放在哪(常量区)
  • 基类和子类的构造和析构顺序(构造先基类后子类,析构相反)
  • 讲一下 map 的实现?具体细节?(红黑树及其性质)
  • move 的实现和原理? (static_cast<std::remove_ref<T> &&>())
  • 如果不是指针呢?比如 move 的是结构体对象,有指针也有 int?(基本类型拷贝,对象进行移动)
  • 进程空间布局(代码段、data 段、bss 段、堆、文件映射区、栈)
  • 段是怎么理解的(内存段,存储代码或者数据)
  • 页表是管理什么的(虚拟内存和物理内存映射关系)
  • 进程线程协程的区别(从资源和切换上答)
  • linux 下进程和线程的调度有区别吗?(没有区别,都用 task_struct 表示,看成一个任务进行调度)
  • 栈和堆有什么区别?(管理方式、空间大小、分配释放、访问效率、生命周期)
  • 栈为什么比堆效率高?
    • 栈只需移动栈帧,堆需要找合适的内存空间
    • 栈帧中的局部变量通常在一段连续区域里,函数执行时访问集中,CPU Cache 容易命中;堆对象经常比较分散,指针跳转多,cache miss 概率更高
    • 堆容易产生额外间接访问
  • 栈内存和堆内存都要找到内存地址,怎么对比速度?
    • 地址计算成本不同
      • 栈变量的地址通常是已知规则的,比如 “SP/BP + 偏移量”,编译器很容易生成指令,定位非常直接。
      • 堆对象的地址通常先要通过分配器申请得到,再通过指针间接访问。对象位置更随机,寻址链路可能更长。
    • 缓存命中率不同
      • 栈上的数据通常连续,当前函数频繁访问,cache 更容易命中
      • 堆上的对象分布离散,尤其链表、树这类结构,容易 cache miss
  • CPU 多级缓存设置的目的是什么?(局部性原理,把近期或频繁使用的数据放到更靠近 CPU、速度更快的存储层里)
  • 那栈和堆,是不是和 CPU 多级缓存是类似的?(栈和堆是进程虚拟地址空间中的内存组织方式; L1/L2/L3 CacheCPU 的硬件存储层次)
  • 并发和并行有什么区别(多个任务切换执行/多个任务同时执行)
  • 阻塞 IO 和非阻塞 IO 区别(等待缓冲时是阻塞还是返回错误)
  • 动态链接和静态链接的区别(运行时链接库/编译时复制库)
  • epoll 的实现(将需要监听的 fd 保存在内核中的一个红黑树中,监听到事件时返回对应的 fd
  • epollselect 的区别(内核红黑树、异步回调、只返回有事件 fd
  • 边缘触发和水平触发(事件发生后只通知一次/只要有数据没读写完就再次通知)
  • 怎么判断事件有没有读写完(读写时会给定数据的真实长度,返回值是真正读写成功的数据长度)
  • 没读写完会产生什么错误码(EAGAIN
  • TCP 四次挥手
  • 客户端 timewait 过多怎么办
    • 产生原因:短连接太多,客户端经常主动关闭连接
    • 解决办法:
      • 减少短链接,替换成长连接;
      • 让服务端主动断开;
      • 扩大客户端可用端口资源 ip_local_port_range
      • 复用 timewait 对应的 socket,内核参数:net.ipv4.tcp_tw_reuse
      • 调整系统所能容纳的 timewait 数量,内核参数:net.ipv4.tcp_max_tw_buckets
  • 一个数据包从网卡到应用层的收包过程
  • 这个过程中软中断是那一部分?
  • 应用层到内核缓冲区读数据会有软中断吗
  • 对这个过程中的 reuseport 有了解吗?
    • reuseport 指的是多个 socket 可以同时 bind 到同一个 IP:Port,数据包一路到传输层后,内核根据四元组找到目标 socket;如果目标端口对应的不是单个 socket,而是一组开启了 SO_REUSEPORTsocket,那么内核会在这组 socket 之间选一个来接收这个连接或数据包。
  • reuseaddr了解过吗?
    • reuseaddr 放宽 bind() 时对本地地址的校验规则,允许更快重用本地地址/端口
    • 最常见的使用场景是:服务端重启。
  • 内核在什么情况会发送 RST(内核认为 “这个报文不属于当前连接” 或者 “本端要直接中止连接” 时)
  • 怎么判断是错误的连接(序列号和确认号)
  • HTTPS 完整的握手流程(RSA 握手)
  • 具体有哪几种算法(RSAECDHE
  • HTTP Session 复用
    • HTTPSSession 复用 其实是 TLS 会话恢复。常见两种:Session IDSession TicketSession ID 是服务端保存会话,客户端下次带 ID 来命中缓存恢复;Session Ticket 是服务端把会话状态加密后发给客户端保存,下次客户端带回来恢复,服务端更省状态,也更适合负载均衡。
  • HTTP 长连接(发送一次请求和响应后不断开)
  • QUIC 了解过吗?(解决TCP队头阻塞、更快连接,端口迁移)
  • 为什么 QUIC 连接更快?
  • 还了解 QUIC 的其他的知识吗?
  • 有性能测试吗,有对比其他服务器比如 Nginx 吗?
  • 服务器接收和响应的数据流是怎么样的,从 accept 开始(accept 是单独一个协程,有新连接就建一个新协程)
  • 为什么用协程(切换开销小,配合非阻塞实现异步)
  • 定时器用什么实现(set
  • 支持主线程调度任务减少开销是什么意思(调度器线程执行完调度任务后也用来执行任务)

参考来源