考点

  • 进程与线程的本质区别
  • 进程调度与线程调度
  • 用户态与内核态、系统调用
  • 进程状态流转
  • 僵尸进程、孤儿进程
  • 进程间通信(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 卡死时,如何结合 pstoppidstatstrace 分析线程状态
    • 为什么系统 load 高不一定意味着 CPU 高
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. 什么是死锁?死锁产生的必要条件是什么?如何避免和排查?
  • 核心要点

    • 死锁是多个执行单元互相等待对方释放资源,导致永久阻塞
    • 四个必要条件:
      • 互斥
      • 请求并保持
      • 不可剥夺
      • 循环等待
    • 常见避免方式:
      • 固定加锁顺序
      • 一次性申请资源
      • 设置超时
      • 减少锁嵌套
      • 使用无锁/低锁设计
  • 易错点

    • 只会背四个条件,不会举实际例子
    • 把“阻塞很久”直接等同于死锁
    • 忽略数据库锁、分布式锁和程序锁之间的联系
  • 深挖点

    • 如何通过线程栈、jstackpstackgdbproc 信息排查死锁
    • 服务“假死”一定是死锁吗,也可能是 IO hang、GC stop-the-world、锁饥饿
    • K8s 环境中一个 Pod 无响应时,如何区分应用死锁还是节点资源问题
Q7. 什么是上下文切换?为什么上下文切换过多会导致系统性能下降?
  • 核心要点

    • 上下文切换是 CPU 从一个任务切换到另一个任务时,保存和恢复执行现场的过程
    • 包括寄存器、程序计数器、栈信息,以及可能涉及内核调度开销
    • 频繁切换会导致 CPU 时间浪费在调度而非真正业务执行上
    • 线程过多、锁竞争严重、IO 密集、抢占频繁都会导致上下文切换增加
  • 易错点

    • 只知道“切换有成本”,但说不出成本来自哪里
    • 认为 CPU 利用率低就一定没问题,可能 CPU 被切换和等待消耗掉
    • 认为协程完全没有上下文切换成本
  • 深挖点

    • 进程切换和线程切换的成本差异在哪里
    • 如何用 vmstatpidstat -wsar -w 观察切换情况
    • K8s 中 CPU limit 设置不合理为什么可能放大调度抖动和切换问题
Q8. forkexec 有什么区别?什么是写时复制(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 高、线程多、服务响应慢”的问题?
  • 核心要点

    • 先看整体:topuptimevmstat
    • 再看进程:ps -eftop -Hp <pid>pidstat
    • 再看线程:定位高 CPU 线程,结合线程栈分析
    • 再看系统调用或阻塞:straceperf/proc
    • 在 K8s 中还要结合 kubectl top、容器 limit/request、节点负载一起看
  • 易错点

    • 一上来就重启,不先保留现场
    • 只看容器指标,不看宿主机资源争用
    • 只看 CPU,不看线程阻塞、IO wait、上下文切换
  • 深挖点

    • 如何把线程 ID 转成 Java 线程栈中的 nid
    • 容器 CPU 高但节点 CPU 不高,可能是什么原因
    • 服务延迟高但 CPU 不高,可能是锁竞争、IO 阻塞、下游超时还是 cgroup 限流

速记笔记

  • 进程:资源分配基本单位;线程:调度基本单位
  • 同进程线程共享地址空间,不同进程隔离更强
  • 线程切换通常比进程切换轻,但线程不一定更高效
  • 进程常见状态:RSDTZ
  • 僵尸进程 = 子进程退出未回收;孤儿进程 = 父进程先退出
  • IPC 常见方式:管道、消息队列、共享内存、信号量、Socket
  • 线程同步常见手段:互斥锁、自旋锁、读写锁、条件变量、原子操作
  • 死锁四条件:互斥、请求并保持、不可剥夺、循环等待
  • 上下文切换过多会浪费 CPU 在调度上
  • fork 是创建子进程,exec 是替换进程映像
  • COW = 写时复制,提升 fork 效率
  • 用户态/内核态切换常见于系统调用、异常、中断
  • 容器本质是进程隔离,不是虚拟机
  • 容器里的 PID 1 要负责信号处理和子进程回收
  • 排障思路:先整体、后进程、再线程、再系统调用、最后结合容器资源限制分析

面试应答建议

  • 先讲定义,再讲区别,再讲生产场景
  • 尽量把概念和 Linux 命令、K8s 场景结合起来
  • 回答时优先体现“排障思维”而不是只背书
  • 如果被追问,优先从:
    • 原理
    • 命令
    • 现象
    • 生产案例
      这四个层次展开