面经2
1.ps命令的常见用法
ps aux
ps -eo: 自定义输出格式
使用ps命令查看某个进程的详细信息:
使用PID: ps -p <PID> -o pid,ppid,cmd,%cpu,%mem
通过进程名查找: ps aux | grep <进程名>
杀死一个进程:
kill -9 + <Pid>: 强制杀死一个进程
pkill <进程名>
2.CPU负载高,怎么排查?
排查高负载是否是高 CPU 利用率引起的:
top或ps aux: 找到高CPU利用率的进程的Pid。
检查:检查该进程是否处于死循环、是否存在计算密集型任务等。
如果是低 CPU 利用率,而出现高负载: 可能是由于IO等待所造成的
通过top命令查看%wa: %wa表示CPU因为IO操作而空闲的时间占比
频繁的上下文切换(进程/线程过多): 内核态的时间长,用户态时间短
通过top命令查看%sys(内核态 CPU 时间,应该会升高),%user(用户态 CPU 时间,降低)
注:上下文切换:当 CPU 从一个进程(或线程) ...
mysql
mysql1.Innodb为什么使用自增id作为主键?自增 ID 作为主键的优点:
InnoDB 的数据是以 B+树 结构存储的,数据被组织成一个个页
不需要频繁调整现有数据的位置:每次插入新记录时,记录会按照 顺序 添加到当前索引节点的后续位置。
减少页分裂和碎片化的问题:
使用自增id作主键:当一页写满时,新的记录会自动开辟一个新的页来存储,而不会影响现有的页结构。
非自增主键: 插入的主键是随机的,插入点可能会落在现有的任意页中,从而可能导致任意的页发生分裂,也容易出现碎片化的问题。
2.MyISAM和InnoDB两种存储引擎在实现 B+ 树索引方式上的区别MyISAM:
MyISAM使用非聚簇索引,索引文件和数据文件是分离的。B+ 树的叶节点存放的是指向实际数据记录的地址,而不是数据本身。
检索过程:
通过 B+ 树搜索算法找到指定 key 的位置。如果该 key 存在,则取出其 data 域中的值,这个值是指向实际数据记录的地址。
根据这个地址去读取相应的数据记录。
InnoDB:
InnoDB使用聚簇索引,索引文件和数据文件是一起存储的。
注:在My ...
面经1
1.单例模式的应用场景?
日志记录器:整个应用程序只需要一个日志记录,可以防止被重复创建。
文件配置初始化类
2.HTTP协议常见状态码
1xx (信息):临时响应 - 已收到请求,正在继续处理。
2xx (成功):服务器已成功接收并接受请求。
3xx (重定向):需要采取进一步操作才能完成请求。
4xx (客户端错误):请求包含错误,无法实现。
5xx (服务器错误):服务器无法完成请求。
200(成功) - 服务器成功返回网页
201(已创建) - 请求成功并且服务器创建了新的资源。
304(未修改) - 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
400(错误请求) - 服务器不理解请求的语法。
404(未找到) - 请求的网页不存在
500(服务器内部错误) - 服务器遇到错误,无法完成请求。
503(服务不可用)- 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
3.前向声明的作用?前向声明主要用于.h文件中,仅告诉编译器某个类的存在,而没有提供类的具体实现。
作用:
减少编译依赖,加快编译速度:当一个头文 ...
网络八股
1.什么是负载均衡?如何实现负载均衡?负载均衡:将工作负载(如用户请求、数据流量等)合理地分配到多个服务器,以优化资源利用率、提高系统性能、增强系统的可靠性和可用性,避免单个服务器过载。
(硬件/软件)负载均衡器:根据负载情况/预定的规则,来将请求转发到后端服务器。
DNS负载均衡:同一个域名可以解析为多个 IP 地址,DNS 服务器会根据一定的策略(如轮询)返回不同的 IP
分配策略:
轮询: 按顺序依次分配请求到服务器。01
加权轮询: 根据服务器的性能设置权重,性能高的服务器分配更多请求。
最少连接(适合长连接场景): 将请求分配给当前连接数最少的服务器。
哈希算法(适合需要会话保持的场景): 根据请求的某些特征(如 IP 地址、会话 ID)进行哈希计算,确保相同特征的请求总是分配到同一台服务器。
2.介绍一下ping命令的原理ping是用于测试网络中主机间是否可达的工具。其基于ICMP协议(网络层,与IP协议配合使用,为IP协议提供了错误报告和控制机制)
ICMP协议:
ping命令的过程:
命令发起: 输入 ping 命令
域名解析(DNS)
构造 IC ...
设计模式面试
设计模式面试1.什么是设计模式?为什么要使用设计模式?设计模式是一种解决问题的方案。
为什么要使用设计模式?
提高代码复用性:经过验证的解决方案可以直接应用于类似问题
提高可维护性:使代码更易于理解和修改
提高扩展性:使系统更容易应对变化和增长
2.设计模式有哪些原则?单一职责原则(SRP):一个类只应该有一个引起它变化的原因。一个类只负责一项职责。
开闭原则(OCP):软件中的对象(类、模块、函数等)应该对扩展开放,对修改关闭。通过扩展来实现变化,而不是修改现有代码。
依赖倒置原则(DIP):为了保证低耦合
高层模块不应该依赖低层模块,二者都应该依赖抽象
抽象不应该依赖细节,细节应该依赖抽象
3.工厂模式和策略模式的区别?
目的不同:
工厂模式的目的是创建对象,将对象的创建过程封装起来,使客户端的代码不需要依赖于具体类的实例化逻辑和使用逻辑。
策略模式的目的是对于行为的封装和替换,使得客户端可以在运行时动态选择不同的算法。
结构不同:
工厂模式:抽象产品类+具体产品类
策略模式:抽象策略类+具体策略类+上下文类(持有一个 抽象策略 的引用,并允许在运行时动态设置不同的 ...
C++八股8
1.类对象的大小由哪些因素决定?
空类或无数据成员类的默认大小:如果一个类不包含任何数据成员(包括继承来的数据成员),根据C++标准,即使是空类也会被分配至少1个字节的空间。这是为了确保每个对象都有唯一的地址。
虚函数的影响(虚函数指针):当一个类包含虚函数时,编译器会为该类添加一个指向虚函数表(vtable)的指针,称为vptr。这个指针是每个对象的一部分,因此它增加了对象的大小。指针的大小取决于系统架构(例如,在32位系统上是4字节,在64位系统上是8字节)。
虚继承的影响:当使用虚继承来解决多重继承下的菱形继承问题时,每个对象需要额外的指针来维护到虚基类子对象的引用,这被称为虚基表指针(vbptr)。这样的设计避免了基类部分的重复,但同时也增加了每个对象的大小。
内存对齐:类对象的大小计算遵循结构体内存对齐的原则。这意味着编译器会在数据成员之间或者末尾插入填充字节,以满足特定硬件平台上的对齐要求。这种对齐可以提高访问速度,但同时可能增加对象的实际大小。
注:静态数据成员和成员函数不影响:无论是静态还是非静态成员函数,它们都不会影响类对象的大小。这是因为成员函数实际上是存 ...
面试模板
1.自我介绍尊敬的面试官,您好。我是东北大学在读硕士研究生XXX。我的技术栈是C++,熟悉windows和linux的C++开发,熟悉网络编程开发和常见的网络协议。
在实践过程中,我实现过一个高性能RPC框架,让我对分布式系统通信和RPC远程过程调用有了进一步的认识,另外,我也做Linux性能监控项目,基于grpc实现了对服务器上的性能数据采集,并进行监控的过程。
在校期间,我曾多次获得过校奖学金,参加过蓝桥杯和程序设计天梯赛,并均获得了国家三等奖,这些经历使我具有扎实的算法基础和工程实践能力,所以我认为贵公司的开发岗位要求和我的技术能力是非常契合的。
最后,非常感谢您给我这次面试机会,谢谢。
2.1 介绍一下你的项目(分布式Linux性能监控)在校期间我做过分布式Linux性能监控的项目,首先,我利用了Docker容器化的技术,通过Dockerfile来构建环境,例如安装CMake、gRpc、Protobuf等依赖,以此来简化多服务器的部署。
通过基于gRpc的框架来构建服务端和客户端,服务端位于目标监控的服务器上,客户端调用monitor模块和display模块实现对服务端的监控, ...
STL面试
STL面试1.vector迭代器失效的问题?vector中的插入/删除会改变元素的位置或数量,从而导致迭代器失效。对vector进行连续插入或者删除操作(insert/erase),一定要更新迭代器。
2.array和vector的区别?
大小是否可变:
std::array在编译时确定大小,运行时无法更改。
std::vector可在运行时动态调整大小
底层实现:
std::array: 基于普通数组(连续存储)实现,直接存储在栈上(除非它是某个堆对象的一部分)。
std::vector: 基于动态数组实现,数据存储在堆上。(在使用vector这个结构的时候,如果vector在函数内部直接定义,则对象存储在栈上,数据存储在堆上;而通过new动态创建时,指针在栈上,对象和数据都在堆上。
性能: vector插入或删除元素时可能需要重新分配内存和复制数据,导致性能下降。
注:如果提前预留足够的容量(通过 reserve() 方法),可以减少动态分配的次数。
容器大小:
std::array: 返回的是编译时固定的大小
std::vector:返回当前存储的元素数量
迭 ...
智能指针面试题
智能指针面试题1.介绍一下shared_ptr的底层实现
std::shared_ptr的底层结构分为两部分:
控制块(Control Block):一个动态分配的小型数据结构,用于存储引用计数和其他元信息。
强引用计数(Strong Reference Count):记录有多少个 shared_ptr 引用了该对象。
弱引用计数(Weak Reference Count):记录有多少个 std::weak_ptr 引用了该对象(用于支持 std::weak_ptr)。
删除器(Deleter):可选的自定义函数,用于释放对象时执行特定操作。
分配器(Allocator):可选的自定义分配器,用于管理内存分配和释放。
原始指针(Raw Pointer):保存一个指向实际对象的原始指针
2.介绍一下shared_ptr的引用计数的操作增加引用计数:当一个 shared_ptr 被拷贝(通过拷贝构造函数或赋值操作符)时,控制块中的强引用计数加 1。
减少引用计数:当一个 shared_ptr 被销毁或重置时,控制块中的强引用计数减 1。
强引用计数变为 0:
调用删除器(如果 ...
Muduo库
非阻塞IO+IO复用Event Loop: 一个不断监听事件的发生,并调用相应回调函数的机制。处理异步I/O操作的编程模式,通过一个无限循环监听和分发事件。
注:在Event Loop中,应用首先注册感兴趣的事件处理器(如网络连接、文件I/O等),然后进入一个循环,等待这些事件的发生,并在事件发生时调用相应的处理器来响应事件。
Non-Blocking 几乎总是和 I/O 多路复用一起使用:
单用Non-Blocking需要轮询检测I/O状态,会浪费CPU资源。
I/O多路复用不能与阻塞I/O共存:I/O 多路复用判断某个 socket 可能处于某种状态(如“可读”或“可写”),但这并不保证后续的阻塞 I/O 操作一定能够成功完成。
对于“可读”状态,可能只是表示内核缓冲区中有一些数据,但实际的数据量可能不足以满足 read() 请求(比如请求读取 100 字节,但缓冲区只有 50 字节)。
对于“可写”状态,可能只是表示内核缓冲区有空间,但如果你尝试写入大量数据,可能会超出缓冲区容量,导致 write() 阻塞。
注:阻塞 I/O 会导致线程被挂起,从而无法处理其他 sock ...