考点
- 进程与线程的本质区别
- 进程调度与线程调度
- 用户态与内核态、系统调用
- 进程状态流转
- 僵尸进程、孤儿进程
- 进程间通信(IPC)
- 线程同步与互斥
- 死锁产生条件与排查
- 上下文切换与性能影响
fork/exec/ 写时复制(COW)- Linux 中查看和排查进程线程问题的方法
- 容器中的进程模型:PID namespace、PID 1、cgroup
Q1. 进程和线程有什么区别?为什么线程切换通常比进程切换更轻量?
核心要点
- 进程是 资源分配的基本单位,线程是 CPU 调度的基本单位
- 同一进程内的线程共享地址空间、文件描述符、全局变量等资源
- 不同进程之间资源隔离更强,通信成本通常更高
- 线程切换通常比进程切换轻,因为不需要完整切换地址空间等资源上下文
- 多线程更适合高并发、共享内存场景;多进程更适合隔离要求高的场景
易错点
- 把“进程是调度基本单位”当成标准答案
- 认为线程一定比进程快,忽略锁竞争、伪共享、上下文切换等问题
- 认为线程间通信“没有成本”,实际上同步开销可能很高
深挖点
- Linux 中线程本质上也是轻量级进程
- 为什么 Java/Go/Python 的并发模型和内核线程的关系不同
- K8s 场景下,服务是多进程还是多线程,对监控、排障、资源隔离有什么影响
Q2. 进程有哪些状态?Linux 中常见的进程状态如何理解?
核心要点
- 常见状态包括:新建、就绪、运行、阻塞、终止
- Linux 常见状态码:
R:运行或可运行S:可中断睡眠D:不可中断睡眠,通常与 IO 等待有关T:停止/跟踪Z:僵尸进程
- 运维排障中要特别关注
D状态和Z状态
易错点
- 把“就绪”和“运行”混为一谈
- 不知道
D状态为什么危险 - 认为
Z状态会大量占用 CPU 或内存,实际上它主要占用 PID 表项
深挖点
- 为什么磁盘、NFS、块设备异常时容易出现大量
D状态 - 一个 Pod 卡死时,如何结合
ps、top、pidstat、strace分析线程状态 - 为什么系统 load 高不一定意味着 CPU 高
- 为什么磁盘、NFS、块设备异常时容易出现大量
Q3. 什么是僵尸进程和孤儿进程?在生产环境中有什么影响?
核心要点
- 僵尸进程:子进程已经退出,但父进程没有调用
wait/waitpid回收 - 孤儿进程:父进程先退出,子进程被
init或其他收养进程接管 - 僵尸进程会占用 PID 表项,数量过多可能导致新进程无法创建
- 孤儿进程本身不一定有害,关键看是否被正常接管和回收
- 僵尸进程:子进程已经退出,但父进程没有调用
易错点
- 把僵尸进程和孤儿进程混淆
- 认为 kill 掉僵尸进程就能解决问题,实际上僵尸进程已退出,核心问题在父进程不回收
- 认为孤儿进程一定是异常
深挖点
- 容器里 PID 1 为什么特别关键
- 为什么有些容器镜像没有正确处理信号和子进程回收,容易产生僵尸进程
- 在 K8s 中如何通过 tini、dumb-init 或正确的 entrypoint 解决 PID 1 问题
Q4. 进程间通信(IPC)有哪些方式?各自适用场景是什么?
核心要点
- 常见 IPC:管道、命名管道、消息队列、共享内存、信号量、信号、Socket
- 管道适合有亲缘关系的进程
- 命名管道可用于无亲缘关系进程
- 共享内存速度快,但要配合同步机制使用
- Socket 通用性最强,跨主机通信也适用
- 信号适合通知,不适合携带复杂数据
易错点
- 认为共享内存一定最好,忽略同步复杂度
- 把信号量当成“传递数据”的机制
- 不清楚本地 IPC 与网络通信的边界
深挖点
- 为什么高性能程序常用共享内存 + 锁/信号量
- 容器内多个进程通信与宿主机进程通信在 namespace 下有什么区别
- K8s 中 sidecar 与主容器通信更常见用什么方式,为什么
Q5. 线程同步有哪些常见方式?互斥锁、自旋锁、读写锁分别适合什么场景?
核心要点
- 常见同步方式:互斥锁、读写锁、自旋锁、条件变量、信号量、原子操作
- 互斥锁适合临界区较长的场景
- 自旋锁适合临界区非常短、线程不会被长时间阻塞的场景
- 读写锁适合读多写少场景
- 条件变量用于线程间等待/通知
- 原子操作适合简单共享变量更新
易错点
- 认为自旋锁一定比互斥锁快
- 在高竞争场景滥用读写锁
- 只知道“加锁”,不知道锁粒度、锁顺序、锁竞争分析
深挖点
- 用户态锁和内核态阻塞的关系
- futex 的基本思想是什么
- 在高并发服务中,锁竞争会如何影响容器 CPU 使用率和延迟抖动
Q6. 什么是死锁?死锁产生的必要条件是什么?如何避免和排查?
核心要点
- 死锁是多个执行单元互相等待对方释放资源,导致永久阻塞
- 四个必要条件:
- 互斥
- 请求并保持
- 不可剥夺
- 循环等待
- 常见避免方式:
- 固定加锁顺序
- 一次性申请资源
- 设置超时
- 减少锁嵌套
- 使用无锁/低锁设计
易错点
- 只会背四个条件,不会举实际例子
- 把“阻塞很久”直接等同于死锁
- 忽略数据库锁、分布式锁和程序锁之间的联系
深挖点
- 如何通过线程栈、
jstack、pstack、gdb、proc信息排查死锁 - 服务“假死”一定是死锁吗,也可能是 IO hang、GC stop-the-world、锁饥饿
- K8s 环境中一个 Pod 无响应时,如何区分应用死锁还是节点资源问题
- 如何通过线程栈、
Q7. 什么是上下文切换?为什么上下文切换过多会导致系统性能下降?
核心要点
- 上下文切换是 CPU 从一个任务切换到另一个任务时,保存和恢复执行现场的过程
- 包括寄存器、程序计数器、栈信息,以及可能涉及内核调度开销
- 频繁切换会导致 CPU 时间浪费在调度而非真正业务执行上
- 线程过多、锁竞争严重、IO 密集、抢占频繁都会导致上下文切换增加
易错点
- 只知道“切换有成本”,但说不出成本来自哪里
- 认为 CPU 利用率低就一定没问题,可能 CPU 被切换和等待消耗掉
- 认为协程完全没有上下文切换成本
深挖点
- 进程切换和线程切换的成本差异在哪里
- 如何用
vmstat、pidstat -w、sar -w观察切换情况 - K8s 中 CPU limit 设置不合理为什么可能放大调度抖动和切换问题
Q8. fork 和 exec 有什么区别?什么是写时复制(COW)?
核心要点
fork会创建子进程,子进程初始时几乎是父进程的副本exec会用新程序替换当前进程的用户空间代码和数据- 典型流程是先
fork,再exec - 写时复制(COW)表示父子进程初始共享物理页,只有发生写入时才复制,提高创建效率
易错点
- 认为
fork后立刻复制全部内存 - 认为
exec会创建一个新进程,实际上通常是替换当前进程映像 - 不清楚父子进程文件描述符继承行为
- 认为
深挖点
- 大内存进程
fork为什么仍然可能带来性能问题 - shell 执行外部命令的底层过程是什么
- 容器运行时启动进程时,和普通 Linux 进程创建有什么共性与差异
- 大内存进程
Q9. 用户态和内核态是什么?线程执行过程中为什么会发生态切换?
核心要点
- 用户态权限较低,不能直接执行特权操作
- 内核态权限高,可访问硬件和内核资源
- 发生系统调用、异常、中断时,会从用户态切到内核态
- 常见如文件 IO、网络 IO、进程创建、内存管理等都需要进入内核
易错点
- 认为用户态到内核态切换就是上下文切换,它们不是一个概念
- 把系统调用和函数调用混淆
- 认为所有性能问题都来自内核态切换
深挖点
- 一次网络请求可能经历哪些态切换
- 零拷贝、
epoll为什么能优化性能 - 在排查高 sys CPU 时,应重点看什么指标和工具
Q10. 容器里的进程和宿主机上的普通进程有什么区别?为什么面试喜欢问 PID 1?
核心要点
- 容器里的进程本质上还是宿主机上的 Linux 进程
- 区别主要来自 namespace 和 cgroup:
- namespace 提供视图隔离
- cgroup 提供资源限制
- 容器内看到的 PID 1 不一定是宿主机 PID 1
- 容器内 PID 1 需要正确处理信号、回收子进程,否则容易产生运维问题
易错点
- 认为容器是“轻量虚拟机”
- 认为容器有自己独立内核
- 不理解为什么应用直接作为 PID 1 可能出问题
深挖点
docker stop/ K8s Pod termination 时信号是怎么传递的- 为什么应用收不到
SIGTERM,可能和 shell 包裹启动、entrypoint 写法有关 - 如何判断一个 Pod 的退出慢是应用处理慢、僵尸子进程、还是 preStop hook 导致
Q11. Linux 中如何排查一个“CPU 高、线程多、服务响应慢”的问题?
核心要点
- 先看整体:
top、uptime、vmstat - 再看进程:
ps -ef、top -Hp <pid>、pidstat - 再看线程:定位高 CPU 线程,结合线程栈分析
- 再看系统调用或阻塞:
strace、perf、/proc - 在 K8s 中还要结合
kubectl top、容器 limit/request、节点负载一起看
- 先看整体:
易错点
- 一上来就重启,不先保留现场
- 只看容器指标,不看宿主机资源争用
- 只看 CPU,不看线程阻塞、IO wait、上下文切换
深挖点
- 如何把线程 ID 转成 Java 线程栈中的 nid
- 容器 CPU 高但节点 CPU 不高,可能是什么原因
- 服务延迟高但 CPU 不高,可能是锁竞争、IO 阻塞、下游超时还是 cgroup 限流
速记笔记
- 进程:资源分配基本单位;线程:调度基本单位
- 同进程线程共享地址空间,不同进程隔离更强
- 线程切换通常比进程切换轻,但线程不一定更高效
- 进程常见状态:
R、S、D、T、Z - 僵尸进程 = 子进程退出未回收;孤儿进程 = 父进程先退出
- IPC 常见方式:管道、消息队列、共享内存、信号量、Socket
- 线程同步常见手段:互斥锁、自旋锁、读写锁、条件变量、原子操作
- 死锁四条件:互斥、请求并保持、不可剥夺、循环等待
- 上下文切换过多会浪费 CPU 在调度上
fork是创建子进程,exec是替换进程映像- COW = 写时复制,提升
fork效率 - 用户态/内核态切换常见于系统调用、异常、中断
- 容器本质是进程隔离,不是虚拟机
- 容器里的 PID 1 要负责信号处理和子进程回收
- 排障思路:先整体、后进程、再线程、再系统调用、最后结合容器资源限制分析
面试应答建议
- 先讲定义,再讲区别,再讲生产场景
- 尽量把概念和 Linux 命令、K8s 场景结合起来
- 回答时优先体现“排障思维”而不是只背书
- 如果被追问,优先从:
- 原理
- 命令
- 现象
- 生产案例
这四个层次展开