- Windows 和 Linux 解决崩溃
Windows:程序崩溃 → 生成 .dmp → WinDbg 打开 .dmp → 加载 exe/dll + PDB → !analyze -v → 看异常线程 → 看调用栈 → 看寄存器/异常地址 → 定位源码
Pdb 全称 Program database,是调试符号文件。pe 文件 和 pdb 文件有一套匹配信息。
Address Space Layout Randomization,地址空间布局随机化。
LInux:core dump:程序崩溃那一瞬间的内存快照和 CPU 现场。 debug symbols 类似于 pdb
一些gdb 命令:bt 调用栈, frame 栈帧
- 野指针、悬空指针和内存泄漏
野指针:指针没有被正确初始化,指向未知地址
悬空指针:指针原来有效,但所指对象已经被释放/销毁
内存泄漏:申请的内存没有释放,同时又失去了访问它的方式
- 处理死锁问题
先确认是不是死锁,然后通过线程栈、core 或数据库 deadlock 日志找到锁等待关系;接着看是不是锁顺序不一致、锁嵌套或者持锁时间过长。解决上优先统一加锁顺序、减少锁粒度和持锁时间,避免持锁做 IO 或调用不可控代码。C++ 多锁场景会优先用
std::scoped_lock,数据库场景会统一事务访问顺序、缩短事务并检查索引。最后还会补充超时、日志和监控,防止类似问题再次发生。
- Pidfd 是怎么解决pid复用的问题的
PID 是可复用的编号,pidfd 是绑定具体进程实例的稳定句柄。拿到
pidfd后,即使原 PID 被复用,pidfd也不会指向新进程。
- C++17 新特性
- std::optional:表示”可能不存在的值”,比如缓存查询、配置项缺省,比”返回 -1 / 空指针”语义清晰。
- std::string_view:只读字符串视图,零拷贝传参,避免临时 string 的堆分配——性能敏感的路径上用。
- 结构化绑定:auto [k, v] = map_entry,解构 pair/tuple,遍历 map 和解析返回结构很方便。
- 动态链接和静态链接区别在哪,各自应用场景是什么
静态链接是在链接阶段把依赖库中的代码直接链接进可执行文件,所以程序体积较大,但部署简单、运行时对外部库依赖少;动态链接则是在可执行文件中保留共享库依赖,运行时由动态链接器加载
.so并完成符号解析。动态链接的优势是多个进程可以共享库代码、库升级方便、程序体积小,但需要处理共享库版本和运行环境问题。实际中,独立工具、嵌入式或者强调可移植部署的程序比较适合静态链接,而大型服务、公共组件和插件系统通常更适合动态链接。
- 讲讲IO 多路复用,epoll 底层是什么
IO 多路复用的本质是用一个线程同时监听大量 fd 的就绪事件,解决”每个连接一个线程”的问题。演进是 select → poll → epoll:
- select:fd 集合是位图,有 1024 上限;每次调用要把全部 fd 从用户态拷贝到内核态,内核线性扫描所有 fd 检查就绪,返回后用户态还要再扫描一遍找就绪的——O(n) 且频繁拷贝,n 大了很浪费。
- poll:把位图换成链表,取消 1024 上限,但仍然是”全量拷贝 + 线性扫描”。
- epoll:改成事件驱动 + 回调,复杂度从 O(n) 变 O(就绪数)。
epoll 的底层结构(以 Linux 内核实现为准):
epoll_create在内核创建一个 eventpoll 对象,核心是两个数据结构:一棵红黑树(存放所有注册监听的 epitem,增删改查 O(log n))和一个就绪链表 rdllist(存放已就绪的 epitem)。epoll_ctl(ADD/MOD/DEL)就是在红黑树上插/改/删节点;ADD 时还会把回调函数注册到被监听文件的等待队列上(ep_ptable_queue_proc→ep_poll_callback)。- 关键在回调:当文件就绪时(比如网卡收到数据,内核把 skb 挂到 socket 接收队列并唤醒),会调用注册的
ep_poll_callback,把该 epitem 挂到就绪链表上,并唤醒在epoll_wait上睡眠的进程。epoll_wait做的事:检查就绪链表,有就直接把就绪事件拷贝到用户态缓冲区返回;没有就把当前进程挂到 eventpoll 的等待队列睡眠,超时或被打醒再检查。因为内核只遍历就绪链表,所以效率与总 fd 数无关。触发模式:LT(水平触发)——只要缓冲区还有数据就每次 epoll_wait 都通知;ET(边缘触发)——只在状态变化(从无到有)时通知一次,所以 ET 必须循环读直到 EAGAIN,否则丢数据。ET 效率更高(通知次数少),但编程要求更高,是经典的坑。
- 进程和线程的区别,什么时候用进程,什么时候用线程
进程是操作系统分配资源的最小单位。线程是cpu 调度的最小单位。同一进程的线程共享代码段、数据、堆和大部分进程资源,但各自有栈、寄存器上下文、调度状态和 TLS。进程之间隔离强,一个进程崩溃或内存破坏通常不会直接覆盖另一个进程,但创建、切换和 IPC 成本更高
多线程:高并发,需要频繁共享数据。 多进程:需要故障隔离…
- 说一些linux 命令
| 目标 | 常用命令 | 关注点 |
|---|---|---|
| 文件 / 磁盘 | pwd、ls -lah、find、stat、du -sh、df -h | du 看目录实际占用,df 看文件系统剩余空间 |
| 文本 / 日志 | grep / rg、head、tail -f、awk、sed、sort、uniq、wc | 先缩小范围,再统计和定位 |
| 进程 / 资源 | ps、pgrep、top / htop、free -h、vmstat、pidstat、lsof | 看进程、线程、CPU、内存、打开的 fd |
| 网络 | ip addr、ip route、ss -lntp、curl -v、ping、nc | 地址路由、监听端口、HTTP 和 TCP 连通性 |
| 调试 | strace、gdb、coredumpctl、dmesg / journalctl | 系统调用、core、内核和服务日志 |
| 权限 / 服务 | id、chmod、chown、sudo、systemctl | 当前身份、权限和服务状态 |
- 讲讲 socket 编程中的 listen 和 accept
客户端 服务端
socket() socket()
| |
| v
| bind()
| |
| v
| listen()
| |
connect() ----------------------->|
| |
| TCP 三次握手 |
|<---------------------------->|
| |
| accept()
| |
send()/recv() <--------------> read()/write()
| |
close() close(connfd)