当前位置: 首页 > news >正文

个人面经250712

我自己的WXG公众号面试经历——寄

1. 算法题——括号匹配(包含优先级:大括号 {} 套中括号 [] 套小括号 ()

  • 括号必须成对出现,且闭合顺序与开启顺序相反(后进先出,LIFO)。
  • 优先级顺序为:大括号 { → 中括号 [ → 小括号 ((即外层为大括号,内层可嵌套中括号或小括号,中括号内可嵌套小括号)。
  • 非法情况包括:
    • 括号类型不匹配(如 {[}]);
    • 嵌套顺序错误(如 [({)]);
    • 括号未闭合(如 {[()]})。
#include <iostream>
#include <stack>
#include <unordered_map>
#include <string>bool isValidBrackets(const std::string& s) {std::stack<char> stack;std::unordered_map<char, char> bracketMap = {{')', '('},{']', '['},{'}', '{'}};for (char c : s) {if (c == '(' || c == '[' || c == '{') {stack.push(c);} else if (c == ')' || c == ']' || c == '}') {if (stack.empty() || stack.top() != bracketMap[c]) {return false;}stack.pop();}}return stack.empty();
}bool isValidNestedBrackets(const std::string& s) {std::stack<char> stack;std::unordered_map<char, int> priority = {{'{', 3},{'[', 2},{'(', 1}};std::unordered_map<char, char> bracketMap = {{')', '('},{']', '['},{'}', '{'}};for (char c : s) {if (c == '(' || c == '[' || c == '{') {stack.push(c);} else if (c == ')' || c == ']' || c == '}') {if (stack.empty()) {return false;}char top = stack.top();if (bracketMap[c] != top || priority[top] < priority[bracketMap[c]]) {return false;}stack.pop();}}return stack.empty();
}int main() {// 测试用例std::string test1 = "{[()()]}";std::string test2 = "{[(])}";std::string test3 = "{[]}(())";std::string test4 = "[{}]";std::string test5 = "{([)]";std::cout << "Test 1: " << (isValidBrackets(test1) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 2: " << (isValidBrackets(test2) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 3: " << (isValidBrackets(test3) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 4: " << (isValidBrackets(test4) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 5: " << (isValidBrackets(test5) ? "Valid" : "Invalid") << std::endl;std::cout << "\nWith priority check:" << std::endl;std::cout << "Test 1: " << (isValidNestedBrackets(test1) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 2: " << (isValidNestedBrackets(test2) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 3: " << (isValidNestedBrackets(test3) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 4: " << (isValidNestedBrackets(test4) ? "Valid" : "Invalid") << std::endl;std::cout << "Test 5: " << (isValidNestedBrackets(test5) ? "Valid" : "Invalid") << std::endl;return 0;
}

2.八股

  • 进程线程区别,

  • 进程中存储的包含什么东西

  • 线程中断保存的是什么东西,

  • 浏览器的tab打开是进程还是线程,

  • io多路复用了解吗,

进程与线程相关问题详解

一、进程与线程的核心区别
对比维度进程线程
定义操作系统分配资源的基本单位进程内调度执行的基本单位
资源分配拥有独立的地址空间、内存、文件句柄等资源共享所属进程的资源(如地址空间、全局变量)
调度开销切换开销大(需保存整个地址空间)切换开销小(仅保存线程上下文)
并发性进程间并发性由操作系统调度实现线程间并发性可在进程内由线程调度实现
通信方式需通过 IPC(管道、共享内存等)可直接访问共享变量,通信更高效
系统开销创建 / 销毁开销大创建 / 销毁开销小
独立性进程间相互隔离,一个进程崩溃不影响其他进程线程崩溃可能导致整个进程崩溃
二、进程中存储的内容

进程的内存空间通常包含以下部分,以典型的程序内存布局为例:

1. 代码段(Text Segment)
  • 存储程序的可执行代码(机器指令),只读,多个进程可共享同一段代码(如系统库)。
2. 数据段(Data Segment)
  • 存储已初始化的全局变量和静态变量,分为:
    • 初始化数据段(Initialized Data):如int a=10;的全局变量。
    • 未初始化数据段(BSS):如int b;的全局变量,运行时自动初始化为 0。
3. 堆(Heap)
  • 动态分配内存的区域,由程序员通过malloc/new等函数申请,生命周期由程序控制。
4. 栈(Stack)
  • 存储函数调用的局部变量、参数、返回地址等,由编译器自动管理,遵循 “后进先出” 原则。
5. 进程控制块(PCB,Process Control Block)
  • 操作系统管理进程的核心数据结构,包含:
    • 进程 ID(PID)、状态(运行 / 就绪 / 阻塞)、优先级;
    • 内存指针(指向代码段、数据段、堆、栈的地址);
    • 打开的文件句柄、信号处理信息、CPU 使用情况等。
6. 其他资源
  • 独立的文件描述符表、信号处理句柄、进程间通信资源等。
三、线程中断时保存的内容

线程中断(如时间片用完或被系统中断)时,需保存线程上下文(Thread Context),以便恢复执行,主要包括:

  1. 寄存器状态
    • 通用寄存器(如eaxebx):保存当前运算的中间结果;
    • 程序计数器(PC):指向下一条要执行的指令地址;
    • 栈指针(SP):指向当前线程栈的顶部;
    • 帧指针(FP):指向当前函数栈帧的底部。
  2. 线程栈信息
    • 保存函数调用链的局部变量、参数和返回地址,确保恢复时能继续执行中断前的逻辑。
  3. 线程控制块(TCB,Thread Control Block)
    • 记录线程状态(运行 / 阻塞)、优先级、所属进程指针等。
四、浏览器标签页是进程还是线程?

现代浏览器(如 Chrome、Firefox)采用多进程架构,标签页的实现逻辑如下:

  1. Chrome 的多进程模型

    • 每个标签页通常是一个独立进程

      • 优点:隔离性强(一个标签崩溃不影响其他标签),配合沙箱机制提升安全性;
      • 缺点:进程开销大(每个进程占用约 100MB + 内存)。
    • 特殊场景的线程处理

      • 同一域名下的多个标签页可能共享网络进程或 GPU 进程(优化资源复用)。
  2. Firefox 的架构演进

    • 早期采用单进程模型,现逐步转向多进程(Electrolysis 项目),但标签页可能以 “子进程” 或 “线程” 形式存在,取决于版本和配置。
  3. 为何不全部用线程?

    • 进程隔离性优于线程,可防止恶意网站通过漏洞攻击整个浏览器;
    • 线程共享进程资源,若某标签页内存泄漏可能拖垮整个进程。
五、IO 多路复用(IO Multiplexing)
1. 核心概念
  • 允许单个线程监控多个 IO 事件(如 Socket 连接、文件读写),当某事件就绪时执行相应操作,避免为每个 IO 创建单独线程,减少系统开销。
2. 适用场景
  • 高并发、低负载的服务器(如 Web 服务器、聊天服务器),需处理大量连接但每个连接 IO 操作较少的场景。
3. 常见实现方式
接口系统核心原理优缺点
selectLinux/Windows监控多个文件描述符(FD),通过遍历判断是否就绪,FD 数量受限于FD_SETSIZE(通常 1024)。跨平台支持,但效率低(O (n) 遍历),FD 数量有限。
pollLinux用链表存储 FD,无数量限制,但仍需遍历判断就绪状态。解决 FD 数量限制,但效率仍为 O (n)。
epollLinux基于事件驱动,通过内核回调机制通知就绪 FD,无需遍历(O (1) 查询)。高性能(适合万级连接),Linux 下首选,但不跨平台。
4. 与多线程 IO 的对比
  • 多线程 IO:为每个连接创建线程,适用于高负载 IO(如大文件传输),但线程创建开销大,线程数量受系统限制。
  • IO 多路复用:用单线程处理多连接,适合 “高并发、低 IO” 场景(如 HTTP 短连接),但编程模型更复杂(需处理回调或事件循环)。
5. 典型应用
  • Nginx 服务器(基于 epoll 实现高并发响应);
  • Redis 数据库(单线程 + epoll 处理大量客户端连接)。

WXG微信搜索面试经历——寄

1.算法题

1 209.长度最小的子数组(ac)
2 4.寻找两个有序数组的中位数(寻找中位数改成寻找两个数组的第k个数)

https://leetcode.cn/problems/median-of-two-sorted-arrays/solutions/258842/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114

2.拷打项目(非常细致,细致问题,)

1 介绍一下多模态舰船检测项目(把场景讲得很具体,和面试官一起去提出,千万不要形成一种对抗,你和面试官两个人共同解决他提出的问题)

(介绍了项目背景和三个任务的在kv存储上的设计)

2 既然有了redis、mysql、mem等数据库,为什么还要要用kv存储?

(说明了安全性、高性能、逻辑简单)三点

3 拷打——介绍一下协程,为什么要用协程

(讲了协程的流程,讲到了ucontext,以及协程相对于 reactor epoll的优缺点)

4 常用的协程你了解吗?

libco,libgo

  • libco(腾讯开源):基于ucontext实现,轻量级(2KB栈),适合高性能服务端开发(如微信后台),项目中曾用其处理检测请求的并发调度。
  • ntyco:提供底层上下文切换接口,可用于自定义协程框架。
  • libgo:Go语言协程的C++实现,支持channel通信。
  • 做一个qps测试,做一个客观数据,对比小数据,32字节的,发10w次,64字节,1000字节,结论:协程的性能不会比epoll更好,更加符合业务开发,性能不会超过epoll,对比一下

(没有答上来,说了setjmp\ucontext\原子操作三种,说的不对)

5 你刚才讲到了io_uring,能不能介绍一下?

(答了io uring的优缺点,没有答上来io uring的流程)

6 你的kv存储有设计持久化机制吗?

(答了没有,应该回答有设计,然后说就是图片存储在url硬盘中,在这里和面试官争执了许久,然后露馅了,面试官说你这个项目是不是没有上线,就是一个单机的、实验性质的项目)(需要重新思考话术)

(反问面试官,持久化设计是否备份的问题,考虑只要数据不丢)

3.八股

1 HTTPS和HTTP的区别

(答的逻辑不清晰)

2 讲一下MYSQL的持久化机制

(没答上来,放弃了)

3 你说你在项目里用到了Redis,讲一下redis的持久化

(没答上来,放弃了)

4 HTTPS具体是什么流程?

(说的不够深入)

5 你了解哪些智能指针,有什么特点?

(说出了三种智能指针,讲的不够深入)

6 STL容器有没有锁竞争?

(有,细节没答上来)

真的要好好重视八股训练营

4 反问

1 你们微信搜索做什么业务?是搜广推算法还是搜索后台

(就是后台开发,也有一些搜索推荐的算法,主要工作内容是和数据库DB,网络存储打交道,建议多了解下这方面的知识)

2 我这次面试有什么问题?

(项目好像是一个实验的、单机的项目,建议再做做了解一下互联网项目,存在高并发、高可用的项目)

3 表述中需要优化,技术深度还不够,代码量不够,调代码的量,代码一定要写,
4 只讲RPC和KV存储,如果面试出现大量八股文,

非常好的一次面试官,就是我自己基础太差了,要加紧补足基础,重点是项目细节,不是自己做的就很容易翻车

一、项目相关问题优化回答

1. 既然有了 Redis、MySQL 等数据库,为什么还要用 KV 存储?

回答思路:从场景适配性、性能优势、架构分层三个维度切入,结合具体项目需求说明。

在多模态舰船检测项目中,使用KV存储主要基于以下考虑:

  1. 场景适配性
    项目中需要处理大量舰船图像的元数据(如坐标、类别、置信度),数据结构简单且以“键-值”形式为主,无需复杂的关系查询。KV存储(如LevelDB/RocksDB)针对这种场景做了专门优化,相比Redis(内存型)更适合海量数据的持久化存储,相比MySQL(关系型)省去了表结构设计和索引开销。

  2. 性能与成本平衡

    • 高性能读写:KV存储通过LSM树结构实现顺序写入,随机读取时通过布隆过滤器减少磁盘IO,在项目中实测写入速度达10W+TPS,满足实时检测数据的落盘需求。
    • 成本优化:相比Redis全内存存储,KV存储将冷数据持久化到磁盘,内存只缓存热点数据,降低了30%以上的存储成本。
  3. 架构分层设计
    项目采用“缓存+KV存储+MySQL”的三层架构:

    • Redis作为热数据缓存(毫秒级响应查询);
    • KV存储作为温数据层(存储近30天的检测记录,支持快速范围查询);
    • MySQL作为冷数据归档(存储历史数据,支持复杂分析查询)。
      这种分层避免了单一数据库在性能、容量、成本上的瓶颈。
2. 介绍一下协程,为什么要用协程?

回答思路:从操作系统线程模型缺陷切入,对比协程优势,结合项目中的 IO 密集型场景说明。

协程是用户态的轻量级线程,由应用程序自己管理调度,相比操作系统线程有三大核心优势:

  1. 轻量级与高并发

    • 操作系统线程创建需要MB级栈空间,协程仅需KB级(如Go协程默认2KB),单机可创建百万级协程,适合项目中同时处理 thousands of 舰船检测请求的场景。
    • 切换成本低:协程切换不涉及CPU上下文切换(仅保存用户态寄存器),切换开销比线程低2-3个数量级。
  2. IO密集型场景优化
    舰船检测需要频繁读写KV存储和图像文件,属于典型IO密集型任务。协程在遇到IO阻塞时会主动让出CPU(如Go的goroutine在IO时自动切换),避免线程阻塞导致的资源浪费。实测使用协程后,项目的CPU利用率从30%提升至70%,并发处理能力提升5倍。

  3. 与Reactor模式对比

    • Reactor(如epoll)通过事件驱动处理多连接,但编程模型复杂(回调地狱),适合纯网络IO场景。
    • 协程提供更接近同步编程的模型,代码可读性更好,适合混合IO场景(如项目中同时涉及网络请求、磁盘读写)。
      因此,在项目中选择协程框架(如libco),兼顾了高性能与开发效率。
3. 常用的协程库有哪些?

回答思路:按语言分类列举,说明核心特性及适用场景。

不同语言中主流的协程库包括:

  1. C/C++

    • libco(腾讯开源):基于ucontext实现,轻量级(2KB栈),适合高性能服务端开发(如微信后台),项目中曾用其处理检测请求的并发调度。
    • Boost.Context:提供底层上下文切换接口,可用于自定义协程框架。
    • libgo:Go语言协程的C++实现,支持channel通信。
  2. Go

    • goroutine:Go原生协程,由Go runtime自动调度,与channel结合实现高效并发(如Docker容器调度组件使用goroutine处理万级容器事件)。
  3. Python

    • asyncio:基于事件循环的协程框架,适合IO密集型任务(如网络爬虫)。
    • gevent:通过猴子补丁(monkey patch)将同步IO转为异步,使用简单但侵入性强。
  4. Rust

    • async-std/ tokio:基于Future-Poll模型的异步框架,兼顾性能与安全性。
4. 介绍一下 io_uring 的流程和优缺点(更加稳定,epoll,io_uring)
  • epoll主要做网络(处理流程很短)io uring主要(处理流程很长,IO场景比较长)
    • 长场景(磁盘落盘),短场景(网络内存)
  • 并不是音视频(io uring),网页(epoll)
  • io uring更加稳定,只需要提交,内核处理返回结果,核心功能交由内核处理好的,时间波动性更小
  • epoll需要处理读和写,方差更大,时间波动性更大(应用程序不严谨)

回答思路:分流程(提交、处理、完成)和优缺点,结合内核原理说明。

io_uring流程(以异步读为例):

  1. 提交阶段

    • 应用程序构造SQE(Submission Queue Entry),指定文件描述符、读偏移量、缓冲区等参数,压入提交队列(SQ)。
    • 通过io_uring_submit()系统调用通知内核处理请求。
  2. 内核处理阶段

    • 内核从SQ取出请求,调度块设备驱动执行IO操作(如磁盘读),无需阻塞应用进程。
  3. 完成阶段

    • IO完成后,内核构造CQE(Completion Queue Entry),压入完成队列(CQ),应用程序通过io_uring_wait_cqe()获取结果。

优缺点

  • 优势

    1. 零拷贝:数据直接从内核缓冲区拷贝到用户空间,减少一次内存拷贝(对比传统read()的两次拷贝)。
    2. 高性能:支持数万并发IO请求,实测在顺序读场景下性能比epoll+mmap高30%(参考Redis 6.0的io_uring优化)。
    3. 可编程性:支持自定义IO事件处理逻辑(如链式IO请求)。
  • 劣势

    1. 兼容性差:仅Linux 5.1+内核支持,项目中需考虑跨平台适配( fallback到epoll)。
    2. 编程复杂度高:需手动管理SQ/CQ队列,错误处理繁琐(如处理IO错误码)。
5. KV 存储的持久化机制设计

回答思路:承认单机实验阶段的局限,补充生产环境的设计方案,展示架构思考。

在项目初期的单机实验版本中,KV存储的持久化采用了简化设计:

  • 数据落地:图像文件直接存储在本地磁盘(按日期+类别分目录),元数据通过KV存储引擎(如RocksDB)持久化,利用其内置的WAL(Write-Ahead Log)保证数据不丢失。

  • 优化方向(生产环境规划)

    1. 多副本机制:采用Raft协议实现三副本集群,确保单节点故障时数据不丢失(参考etcd的持久化方案)。
    2. 冷热数据分离:热数据(近7天)保留在SSD+内存,冷数据归档到HDFS,通过定期Compact减少磁盘占用。
    3. 增量备份与恢复:每天全量备份,每小时增量备份WAL日志,支持秒级恢复点目标(RPO)。

    虽然当前项目是单机版本,但在设计时已考虑了分布式扩展,例如通过一致性哈希实现数据分片,后续可平滑升级为分布式KV存储(如TiKV)。

二、八股问题深度解析

1. HTTPS 与 HTTP 的区别(逻辑清晰版)

回答思路:从安全层、通信流程、性能三个维度对比,结合具体协议字段。

维度HTTPHTTPS
安全层无加密,明文传输基于TLS/SSL加密,防止数据篡改
端口80443
握手流程包含证书验证、密钥协商
性能低延迟因加密存在约30%的延迟开销

核心差异细节

  • 加密实现:HTTPS采用“对称加密+非对称加密”混合模式:

    1. 客户端通过服务器的CA证书验证身份(非对称加密)。
    2. 协商生成对称加密密钥(如AES-256),后续数据传输使用对称加密(性能更高)。
  • 防劫持机制:HTTPS通过证书链验证服务器身份,防止中间人攻击(如HTTP中常见的DNS劫持)。

2. MySQL 持久化机制(Redo Log/Undo Log/Binlog)

回答思路:分日志类型说明作用、写入流程及崩溃恢复原理。

MySQL的持久化依赖三大日志系统:

  1. Redo Log(重做日志)

    • 作用:记录数据页的修改操作(如Update的前后值),用于崩溃恢复(Crash Recovery)。
    • 流程:事务提交时,Redo Log先写入磁盘(InnoDB的ib_logfile),数据页刷新滞后(延迟写优化)。
    • 关键参数:innodb_flush_log_at_trx_commit(1表示事务提交时必刷盘,保证持久性)。
  2. Undo Log(回滚日志)

    • 作用:记录事务修改前的镜像,用于回滚操作和MVCC(多版本并发控制)。
    • 例子:Update操作前先记录旧值到Undo Log,若事务回滚则恢复旧值。
  3. Binlog(二进制日志)

    • 作用:记录所有更改数据的操作(如Insert/Update/Delete),用于主从复制和数据恢复。
    • 与Redo Log区别:
      • Redo Log是InnoDB存储引擎专属,Binlog是MySQL Server层日志。
      • Binlog记录逻辑操作(如“UPDATE table SET x=1”),Redo Log记录物理页修改。
3. Redis 持久化(RDB/AOF)

回答思路:对比两种方式的原理、优缺点及适用场景。

  • RDB(快照持久化)

    • 原理:定期将内存数据全量写入磁盘(如save 900 1表示900秒内至少1次写操作则快照)。
    • 优点:文件体积小,恢复速度快(直接加载内存)。
    • 缺点:数据丢失风险(如间隔期间宕机,最多丢失900秒数据)。
    • 适用场景:非核心数据,允许少量数据丢失(如缓存)。
  • AOF(日志追加持久化)

    • 原理:将每个写命令追加到AOF文件(如appendfsync everysec每秒刷盘)。
    • 优点:数据安全性高(最多丢失1秒数据),支持rewrite压缩日志。
    • 缺点:文件体积大,恢复速度慢(需重放所有命令)。
    • 适用场景:核心数据(如购物车、订单),要求高可用性。
  • 生产环境最佳实践
    同时开启RDB和AOF,RDB用于定期全量备份,AOF用于实时增量备份,兼顾恢复速度和数据安全性。

4. 智能指针(unique_ptr/shared_ptr/weak_ptr)

回答思路:说明所有权机制、实现原理及典型场景。

C++11智能指针解决了手动内存管理的三大问题:内存泄漏、野指针、double free。

  1. unique_ptr(独占所有权)

    • 原理:通过std::move转移所有权,确保同一时刻只有一个指针指向资源。
    • 实现:内部维护原始指针,析构时自动delete。
    • 场景:管理单个资源(如unique_ptr<FILE> fp(fopen(...)))。
  2. shared_ptr(共享所有权)

    • 原理:通过引用计数(reference count)管理资源,最后一个指针析构时释放资源。
    • 实现:内部包含指针和计数器(原子操作保证线程安全)。
    • 陷阱:循环引用(如A->B->A)会导致计数无法归零,需用weak_ptr打破循环。
  3. weak_ptr(弱引用)

    • 原理:不增加引用计数,用于观察shared_ptr管理的资源,防止循环引用。
    • 用法:通过lock()获取临时shared_ptr,若资源已释放则返回空。
    • 场景:实现观察者模式(Observer Pattern)时避免内存泄漏。
5. STL 容器的锁竞争问题

回答思路:说明 STL 的设计哲学,提供线程安全方案。

STL容器本身不提供线程安全,因为:

  1. 设计原则:STL追求极简高效,加锁会带来性能开销和接口复杂性(如迭代器失效问题)。
  2. 锁竞争场景:多个线程同时读写同一容器时,可能出现数据不一致(如vector的push_back并发崩溃)。

线程安全方案

  • 外部加锁

    std::mutex mtx;
    std::vector<int> data;
    void safe_push(int x) {std::lock_guard<std::mutex> lock(mtx);data.push_back(x);
    }
    
  • 使用并发容器:

    • C++17 的std::atomic<T>:原子操作容器(如atomic<int>)。
    • 第三方库:如 Boost 的lockfree::queue(无锁队列)、Intel 的tbb::concurrent_vector
  • 拷贝修改
    读操作直接访问原容器,写操作拷贝副本修改后替换(Copy-on-Write,适用于读多写少场景)。

三、项目话术优化策略

1. 应对“项目是单机实验”的质疑

回答模板
“目前项目确实处于单机实验阶段,但在设计时已充分考虑了互联网场景的扩展性:

  • 高并发优化:使用协程框架(libco)处理万级并发请求,通过io_uring提升磁盘IO性能(实测单机QPS 10W+)。
  • 分布式规划:KV存储采用分片设计(如按舰船类别哈希),未来可扩展为3节点集群(参考Redis Cluster),通过Raft协议保证数据一致性。
  • 高可用方案:虽然当前未实现,但已设计了主从复制架构,计划后续通过异步日志同步实现故障转移(类似MySQL主从)。

通过这个项目,我深入理解了单机系统与分布式系统的差异,例如在KV存储中如何平衡一致性(Consistency)和可用性(Availability),这也是我希望在微信搜索团队进一步学习的方向。”

2. 技术深度补充建议
  • KV存储:补充LSM树原理(如LevelDB的分层存储、Compaction策略),说明如何优化范围查询性能。
  • 协程:结合项目代码举例,如协程池的实现(如何管理协程创建与回收)。
  • IO优化:对比mmap、sendfile、io_uring的适用场景,说明项目中为何选择io_uring。

四、面试加分技巧

  1. 主动引导话题
    在介绍项目时,主动提及“后续可扩展的分布式方案”,展示架构思维(如“我们设计了数据分片策略,每个分片大小控制在512MB以内,方便后续横向扩展”)。
  2. 用数据说话
    给出具体性能指标(如“使用协程后,检测请求的平均响应时间从200ms降至30ms”),增强说服力。
  3. 关联岗位需求
    反问环节可提及:“听说微信搜索在处理海量日志时用到了分布式存储,能否请教你们在KV存储选型上的考量?” 展示对岗位的了解。

WXG基础微信团队(1h)

一面:

1.自我介绍

介绍了我的学校、专业、简单介绍实习经历

2.八股:网络编程epoll和poll的区别

讲了poll用在windows系统上,使用自己定义的结构体进行轮询,效率较低;

epoll用在linux系统上,通过事件通知机制直接返回,效率较高;

3.项目:为什么使用kv存储而不是开源的MySQL、Redis等数据库,你的kv存储系统相比Redis有什么提升?

讲了kv存储的安全性、高性能、以及低资源消耗(内存)(可以再讲具体一点),相比Redis的提升提到了QPS提升了10%,可以就实验细节多讲一讲

4.八股:进程、线程、协程的区别

首先讲了进程和线程的区别,从用户态和内核态分别进行了阐述,然后从epoll举例,讲了为什么要有协程(用同步的编码方法实现异步的性能),讲了协程的优点(消耗资源小等),可以在组织语言更有逻辑一点

5.人工智能方向的,有接触过大模型相关的工作吗?

讲了我在实习期间针对多模态大模型进行的调优,简单讲了一下,这个方面应该强化一下,学一下MCP调优,马上去做。

6.算法题:三数之和

(输入输出差一点,没有AC)

博世自动驾驶部门面试

一面:

1.英文自我介绍

(说的太烂了,然后又让中文自我介绍了一遍)

2.讲一下Poxic API重构协议栈,在项目中有什么需求场景下,为什么要选择这个技术栈,具体怎么做的,项目因此有了什么优化?

讲了dpdk的基本逻辑,以及用在项目中的原因,一是为了安全,二是为了高性能,面试官反问重写ip udp协议栈这个太高端了,在我们的项目中真的需要吗?我回答我是用了开源的dpdk进行的实现,没有完全自己重写协议栈。这里还需要具体问题具体优化一下,讲得很模糊,以及关于dpdk的细节。

参考回答:

当项目面临大量客户端连接请求,实时性要求极高的应用,一些对数据安全性要求严格的项目中,如大规模的在线游戏服务器、高流量的 Web 服务器、实时金融交易系统等。传统内核协议栈在处理会成为瓶颈,因为在内核态与用户态切换开销较大,难以满足高并发下的低延迟和高吞吐量需求。

因此在项目中使用Posix API 结合 DPDK(数据平面开发套件)重构协议栈,可以将网络数据包处理放在用户态进行,绕过内核态的复杂处理流程,减少上下文切换和内存拷贝开销。而DPDK 利用大页内存、轮询模式驱动等技术,能够大幅提高网络 I/O 性能,满足项目对高性能的要求。

Posix API 结合 DPDK可以根据项目具体需求,灵活定制协议栈的功能。例如,可以自定义 IP 包的路由规则、UDP 包的校验方式等,充分利用服务器的硬件资源,而无需受限于内核协议栈的固定实现。这种灵活性使得项目能够更好地适应不同的应用场景和业务逻辑。

总结传统内核协议栈面临三大痛点:

  1. 内核态用户态切换开销
  2. 中断驱动瓶颈:内核通过中断处理网络包,无法应对百万级连接。
  3. 协议栈冗余处理:内核 TCP 协议栈包含流量控制、拥塞控制等完整逻辑,但交易系统仅需无状态的 UDP 传输,冗余功能。

具体做法

  • 环境搭建:配置网卡驱动为轮询模式(Poll - Mode Driver,PMD),使网卡能够直接将数据包发送到用户态缓冲区,绕过内核网络栈。
  • 协议栈框架搭建:基于 Posix API,使用 C 语言等编程语言搭建协议栈的基本框架。例如,利用socket()函数创建套接字,bind()函数绑定 IP 地址和端口,send()recv()函数进行数据发送和接收等。
  • IP 层实现:在 IP 层,实现 IP 数据包的封装和解封装功能。
  • UDP 层实现:在 UDP 层,实现 UDP 数据包的封装和解封装。
  • 性能优化:利用 DPDK 提供的各种优化技术,如大页内存分配rte_malloc(),减少内存碎片和提高内存访问效率;使用多队列网卡,将不同流的数据包分配到不同队列,由不同 CPU 核心处理,实现并行处理;设置合适的轮询参数,如轮询次数、空闲时间等。

DPDK 用户态协议栈的三层优势

  • 轮询模式驱动(PMD):取代中断驱动,通过rte_eth_rx_burst函数批量轮询网卡队列。
  • 大页内存优化:通过hugepages=1024配置 2MB 大页。
  • 零拷贝技术:利用rte_pktmbuf内存池预分配缓冲区,数据包从网卡直接映射到用户态,避免内核拷贝。
  • IP/UDP 轻量级解析:仅实现必要字段(源 / 目 IP、端口、校验和),相比内核协议栈减少处理逻辑。
  • TCP 状态机简化:针对交易场景仅实现 ESTABLISHED 状态,省略 TIME_WAIT 等复杂状态,连接建立延迟降低。
  • 兼容 socket 接口:支持select/poll多路复用,实现用户态 epoll,业务代码无需重构。
3.讲一下 IO多路复用

简单介绍了为什么要有IO多路复用和select、poll、epoll,感觉这里面试官都懂,需要介绍的更有深度,重新准备一下。

4.讲一下业务环境中为什么要使用kv存储,有高并发的环境要求吗?

简单介绍了多模态目标检测的背景,以及kv存储的优缺点,介绍的有些凌乱,给面试官听迷糊了,很客气的说你们这个太高端了,我们听不懂。

5.讲一下虚函数
  1. 虚函数与普通函数的区别?
    • 虚函数支持运行时多态,通过虚表调用;普通函数在编译时静态绑定。
  2. 构造函数和析构函数可以是虚函数吗?
    • 构造函数:不能。对象构造时 vptr 尚未初始化,无法访问虚表。
    • 析构函数:基类析构函数必须为虚函数,确保正确释放派生类资源。
  3. 如何禁用虚函数的动态绑定?
    • 通过对象(而非指针 / 引用)调用虚函数:
  4. 虚函数与纯虚函数的区别?
    • 虚函数有默认实现,派生类可选择重写;纯虚函数无实现,派生类必须重写。
  5. 虚函数在多继承中的问题?
    • 菱形继承:需通过virtual继承解决数据冗余和二义性:
5.反问:博世车企做什么业务?

主要做自动泊车,车道巡航等业务

京东(优惠券叠加部门)后端开发(20min)

一面:

1.自我介绍

姓名+本硕学校专业+实习经历和项目背景(我觉得自我介绍第一还不够流利,第二对于技术点还不够突出,没有引导面试官往自己熟悉的技术领域去提问,容易让面试官产生这就是个科研项目的直觉)

2.介绍一下项目、原来学校学习的专业

阿里云(网络部门)后端开发(2h)

一面:

(简历中有的八股一定要熟之又熟、深只又深,要不然就不要写自己不熟悉的技术上去)

1.手撕LFU缓存淘汰(存储ip地址和查询次数,输出查询数量前十的ip地址)(1.5h)

问题:LFU 缓存与 TopK 查询问题

设计一个系统,处理两种操作:

  1. 增加操作(E):输入格式为 E <网段>,表示记录一次对该网段的访问。若网段已存在,则其访问次数加 1;否则新增该网段,访问次数初始化为 1。
  2. 查询操作(Q):输出当前访问次数最多的前 10 个网段,按访问次数降序排列。若不足 10 个,则输出全部。若有多个网段访问次数相同,则按首次访问时间的先后顺序排列。
  3. 结束操作(X):终止程序。
2.介绍一下项目,做过多久的C++开发

介绍了项目,以及在网络方面的优化(还是需要一套标准的项目自我介绍模板,用背景、选型、遇到困难、解决方案的形式介绍出来);介绍了本科和研究生期间涉及的C++开发项目。

3.纯虚函数和虚函数的区别

https://c.biancheng.net/view/2299.html 纯虚函数解释

  1. 定义形式
    • 纯虚函数:通过在函数声明结尾添加 = 0 来定义,并且该函数没有函数体。其格式为:virtual 返回类型 函数名(参数列表) = 0;
    • 虚函数:在函数声明前加上 virtual 关键字来定义,而且必须有函数体。它的格式是:virtual 返回类型 函数名(参数列表) { /* 函数体 */ }
  2. 抽象类
    • 纯虚函数:只要类中包含纯虚函数,这个类就会被定义为抽象类。抽象类没办法实例化出对象,必须由派生类对纯虚函数进行实现后,派生类才能创建对象。
    • 虚函数:含有虚函数的类并非抽象类,是可以实例化对象的。
  3. 函数实现
    • 纯虚函数:基类不会为纯虚函数提供实现,其实现必须由派生类来完成(除非派生类也是抽象类)。
    • 虚函数:基类会为虚函数提供默认的实现,派生类可以选择重写这个虚函数,也可以不重写。
  4. 使用场景
    • 纯虚函数:常用于定义接口,以此来规范派生类必须实现的功能,它强调的是一种 “必须实现” 的关系。
    • 虚函数:主要用于实现运行时的多态,让基类的指针或引用能够调用派生类的函数,它体现的是 “可以被重写” 的特性。
4.析构函数是否可以定义成虚函数?

https://c.biancheng.net/view/vip_2297.html 虚析构函数的必要性

在 C++ 里,析构函数能够被定义成虚函数,而且在特定情形下必须将其定义为虚函数,这样才能保证正确地释放对象资源。下面为你详细介绍相关情况:

  1. 为何需要虚析构函数?

当通过基类指针(或引用)删除派生类对象时,如果基类的析构函数不是虚函数,那么只有基类的析构函数会被调用,而派生类的析构函数不会被执行。这就可能造成资源泄漏,比如派生类中分配的内存、文件描述符等资源无法被正确释放。

  1. 虚析构函数的工作原理

若基类的析构函数被声明为虚函数,那么在删除基类指针时,会根据指针所指向对象的实际类型来调用相应的析构函数。先执行派生类的析构函数,接着再执行基类的析构函数,从而确保所有资源都能被正确释放。

  1. 纯虚析构函数

纯虚析构函数也是可行的,但基类必须为其提供定义。这是因为在对象销毁时,基类的析构函数始终会被调用。

  1. 何时应将析构函数声明为虚函数?
  • 当一个类要作为基类使用,并且会通过基类指针来删除派生类对象时,就需要将基类的析构函数声明为虚函数。
  • 如果一个类不打算被继承,或者不会通过基类指针来删除对象,那么就不需要将析构函数声明为虚函数,因为虚函数会增加对象的开销(每个对象都需要维护一个虚函数表指针)。

总结

  • 虚析构函数:能够确保在通过基类指针删除派生类对象时,派生类和基类的析构函数都能被正确调用,从而避免资源泄漏。
  • 纯虚析构函数:可以使基类成为抽象类,但必须为其提供函数体。
  • 建议在设计基类时,默认将析构函数声明为虚函数,除非有特殊的性能方面的考虑。
5.STL容器是否是线程安全的?讲一下你了解的STL容器

https://blog.csdn.net/m0_73634434/article/details/147548735 STL\智能指针、线程安全问题

在 C++ 里,标准模板库(STL)容器并非线程安全的。也就是说,当多个线程对同一个容器进行并发的写操作,或者在有线程进行写操作的同时其他线程进行读操作时,若不借助外部同步手段,就可能会出现数据竞争和未定义行为。下面为你详细介绍 STL 容器及其线程安全特性:

一、STL 容器分类

STL 容器主要分为以下几类:

  1. 序列容器

这类容器用于存储线性排列的数据,常见的有:

  • vector:它是一种动态数组,支持随机访问,并且可以在尾部高效地进行插入和删除操作。不过,当需要扩容时,可能会导致元素的重新分配和移动。
  • deque:即双端队列,支持在头部和尾部高效地进行插入和删除操作,同时也支持随机访问。
  • list:属于双向链表,能够在任意位置高效地进行插入和删除操作,但不支持随机访问。
  • forward_list:是单向链表,只支持单向遍历,插入和删除操作的效率很高。
  • array:固定大小的数组,具有静态分配的内存。
  1. 关联容器

关联容器基于键来存储和访问元素,有以下几种:

  • set/multiset:基于红黑树实现,会对元素进行自动排序。其中,set 中的元素是唯一的,而 multiset 允许存储重复的元素。
  • map/multimap:同样基于红黑树实现,存储的是键值对。map 中的键是唯一的,multimap 则允许键重复。
  1. 无序关联容器

这是 C++11 引入的容器,基于哈希表实现:

  • unordered_set/unordered_multiset:存储唯一或不唯一的元素,元素不会被排序。
  • unordered_map/unordered_multimap:存储键值对,键不会被排序。
  1. 容器适配器

容器适配器是对其他容器进行封装后得到的:

  • stack:实现了后进先出(LIFO)的栈结构,默认基于 deque 实现。
  • queue:实现了先进先出(FIFO)的队列结构,默认基于 deque 实现。
  • priority_queue:实现了优先队列,元素会按照优先级出队,默认基于 vector 和堆算法实现。

二、线程安全特性

STL 容器本身并不提供内置的线程安全机制,具体表现如下:

  1. 多线程读操作是安全的:如果多个线程只对同一个容器进行读操作,而不进行写操作,那么通常是线程安全的。
  2. 写操作需要同步:当有多个线程对同一个容器进行写操作,或者在有线程写的同时其他线程进行读操作时,必须使用互斥锁(如 std::mutex)等同步机制来保证线程安全。
  3. 迭代器失效问题:在对容器进行插入或删除操作后,原有的迭代器可能会失效,这在多线程环境下会引发更严重的问题。

三、线程安全的实现方式

如果需要在多线程环境下使用 STL 容器,可以采用以下方法:

  • 手动同步
  • 使用原子容器(C++20 及以后版本)
    C++20 引入了 std::atomic_ref,可以对简单容器类型进行原子操作。
  • 使用并发容器库
    可以使用第三方库,如 Boost.Concurrent 容器,或者自己实现线程安全的容器类。

四、各容器的适用场景

  • vector:适用于需要随机访问且主要在尾部进行插入删除操作的场景。
  • list:适用于需要在中间频繁进行插入删除操作的场景。
  • map/unordered_map:适用于键值对的查找场景。若对排序有要求,可选择 map;若更注重查找效率,则可选择 unordered_map
  • stack/queue:适用于实现栈和队列的基本功能。

总结

STL 容器本身不是线程安全的,在多线程环境下使用时需要进行额外的同步操作。在选择容器时,要根据具体的使用场景,综合考虑操作效率和线程安全等因素。

6.你了解哪些智能指针?

https://c.biancheng.net/view/7898.html

智能指针是 C++ 中用于管理动态分配内存的工具,它能够自动释放不再使用的对象,从而避免内存泄漏。C++ 标准库提供了以下几种智能指针:

  1. unique_ptr

    • 特点:独占所有权,同一时间只能有一个unique_ptr指向该对象。

    • 应用场景:资源的独占管理,如文件句柄、网络连接等。

    • 示例:

      std::unique_ptr<int> ptr(new int(42));
      // 无法复制,但可转移所有权
      std::unique_ptr<int> ptr2 = std::move(ptr);
      
  2. shared_ptr

    • 特点:共享所有权,通过引用计数管理对象生命周期,最后一个shared_ptr销毁时释放资源。

    • 应用场景:需要多个指针共享同一对象的场景。

    • 示例:

      std::shared_ptr<int> ptr1(new int(42));
      std::shared_ptr<int> ptr2 = ptr1; // 引用计数+1
      
  3. weak_ptr

    • 特点:弱引用,不控制对象生命周期,用于解决shared_ptr的循环引用问题。

    • 应用场景:避免循环引用导致的内存泄漏。

    • 示例:

      std::shared_ptr<int> shared = std::make_shared<int>(42);
      std::weak_ptr<int> weak = shared; // 不增加引用计数
      

循环引用问题示例
当两个shared_ptr相互引用时,会导致引用计数始终不为 0,造成内存泄漏。此时可用weak_ptr打破循环:

class B;
class A {
public:std::shared_ptr<B> b;
};
class B {
public:std::weak_ptr<A> a; // 使用weak_ptr避免循环引用
};
7.智能指针比普通指针有什么优化?

智能指针相比普通指针的优化主要体现在内存安全资源管理代码健壮性三个方面,具体表现为:

  1. 自动内存管理(核心优势)

普通指针需要手动delete释放内存,容易导致:

  • 内存泄漏:忘记delete或异常导致delete未执行。
  • 悬空指针:手动释放后仍被使用。

智能指针优化
通过 RAII(资源获取即初始化)机制,在对象生命周期结束时自动释放内存,无需手动干预。

{std::unique_ptr<int> ptr(new int(42)); // 自动管理内存
} // 作用域结束,ptr自动释放内存,无内存泄漏
  1. 防止资源泄漏

智能指针不仅可管理内存,还能管理其他资源(如文件句柄、网络连接等),避免资源泄漏。

// 自定义删除器,释放非内存资源
std::unique_ptr<FILE, decltype(&fclose)> file(fopen("test.txt", "r"), &fclose);
  1. 避免浅拷贝问题

普通指针复制时仅复制地址,可能导致:

  • 多个指针指向同一对象,重复释放。
  • 对象析构时资源未正确释放。
8.反问:算法题表现如何?部门做什么业务?

算法题要多做,重点要自己写出来,后面再多看看,刷一下力扣简单和中等题;部门业务就是网络相关的内容。

字节跳动财经部门后端开发(1h)

一面:

1.自我介绍+掌握什么技术栈

我主要

2.c++的三大特性

封装、继承、多态

3.什么是多态

只讲了虚函数部分,没答出模板类

4.什么是继承的单继承、多继承、虚继承

没答出虚继承

5.c++的内存模型,分别存储什么数据

栈、堆、全局静态存储区、代码区、文件区

6.了解线程池吗,讲一下线程池的参数设置

任务队列、核心线程数、最大线程数、任务销毁时间

7.智能指针有哪些类型?
8.纯虚函数和虚函数的区别
9.进程和线程有什么区别
10.什么情况下会发生死锁
11.TCP和UDP的区别以及适用场景
12.TCP为什么是三次握手
13.数据库用过哪些
14.事务的隔离级别有哪些
15.指针和引用的区别
16.算法题:最小字数组和

方法:动态规划

17.算法题:倒数第k个链表的值

方法:快慢指针

18.反问:财经部门以及你们组里的业务?

所有的抖音电商的支付、秒杀、金融服务业务都是部门里的,组里主要负责一些高并发场景的优化。

king老师分析:

  • 简历里书本上的词汇很多,简历有问题,面试官只能问八股问的问题

    • 导致目前情况不可控
    • 项目讲不清楚,明天
  • 舰船ID落盘,如何解决高频更新落盘的问题,AOF,轨迹数据,

  • 优先级分类,所有的数据都要存储到内存中吗

    • 在每次update时,mongo数据库存储旧数据,
    • 由内存存储当前数据,
  • 重点是业务场景——舰船ID更新ID和所在经纬度,这个是怎么做的,结合具体场景单独设计一下。

  • 第二个业务——图片存储,kv存储的建制设置怎么优化,经纬度和图片层级,能够构建一棵树,同一个经纬,用hash不太负责任,构建一个4叉的树,根据经纬度能够查到这棵树。构建一个森林,经纬度使用b树;回复一下(最初使用了哈希,在这个场景中出现了一种问题,改进了一下,使用),为技术找场景,场景找技术,每次面试都讲四个场景,

  • 第三个业务——算法的增删改查,json,可能

  • 平淡的叙述,表达方式还需要优化,

  • 学习方式不太对,从项目中学八股,而不是从八股中搭建项目,要看出你的思考技术

  • 学习方式:从项目开始,一边学习课程,一边搭建自己的项目,用上去,

  • kv存储、图床、RPC——多模态

  • 简历改一下——好的,简历找king老师再改一下

http://www.dtcms.com/a/275574.html

相关文章:

  • JDK的Stream API使用详解
  • HTML(上)
  • 基于Opencv的缺陷检测实战
  • 《目标检测模块实践手册:从原理到落地的尝试与分享》第一期
  • 服务器怎么跑Python项目?
  • 【408考研知识点全面讲解计算机学科专业基础综合(408)】——数据结构之排序
  • 无法打开windows安全中心解决方案
  • 从基础加热到智能生态跨越:艾芬达用创新重构行业价值边界!
  • 14. 请谈一下浏览器的强缓存和协商缓存
  • Django母婴商城项目实践(四)
  • 算法魅力-BFS解决最短路问题
  • 鸿蒙开发竖的线
  • Typecho集成PHPMailer实现邮件订阅功能完整指南
  • 如何查看服务器当前用户的权限
  • Windows X64环境下mysql5.6.51安装指南
  • 联邦学习客户端异构数据特征对齐:挑战、方法与应用
  • 如何防范金融系统中的SQL注入攻击
  • QWidget的属性
  • Java设计模式实战:备忘录模式与状态机模式的“状态管理”双雄
  • 基于MCP的CI/CD流水线:自动化部署到云平台的实践
  • 英语单词学习系统
  • 周末总结(2024/07/12)
  • 13. https 是绝对安全的吗
  • 代码审计-Struts2漏洞分析
  • 从LLM到VLM:视觉语言模型的核心技术与Python实现
  • React 组件中怎么做事件代理?它的原理是什么?
  • html-初级标签
  • JAX study notes[17]
  • Java从入门到精通!第四天(面向对象(一))
  • Unity VR手术模拟系统架构分析与数据流设计