Linux、内核与性能工程¶
面对 Linux 主机“变慢”,如何建立而不是猜测排障证据链?¶
技术说明
先定义“慢”的对象、时间窗与基线:请求延迟、批任务完成时间还是交互卡顿;再按 USE1(Utilization、Saturation、Errors)检查 CPU2、内存、磁盘、网络等资源。uptime/vmstat 1 看运行队列、交换和上下文切换,pidstat -u -r -d -w 1 定位进程,iostat -xz 1 看设备延迟和队列,ss -s、sar -n DEV,TCP 1 看连接与重传。瞬时快照必须结合监控时间序列,不能把 load、%util 或 free memory 单独当结论。
随后从资源症状下钻到调用路径:确认容器 cgroup3 限额,比较应用延迟、调度等待、块 I/O4 延迟和网络 RTT5;用 /proc/<pid>、strace -ttT -p 做短时采样,必要时用 perf 或 eBPF6 聚合栈。边界是观测本身会有成本:生产上限制采样频率、时长与范围;先保存时间、版本、部署和流量变化,确保证据能与事件关联。
实践经验
复合场景中,API7 的 P998 翻倍但主机 CPU、内存和磁盘都在基线内。按版本与请求类型下钻后,只有写请求的“获取数据库连接”阶段变慢;应用实例扩容恰好把每实例连接池相乘到数据库会话上限。团队先冻结继续扩容、为低优先级写入做负载削减并缩小单池上限,副作用是应用侧排队提前暴露。长期从数据库安全并发反推全服务连接预算,扩缩容同步调整池大小,并把池等待、总会话和业务尾延迟串成告警。这个案例说明通用排障应先用对照和分阶段计时缩小范围,而不是看到某项资源有余量就结束调查。
面试回答要点
先界定业务症状、时间窗和变更,再看资源。
先定义“慢”的对象、时间窗与基线:请求延迟、批任务完成时间还是交互卡顿;再按 USE(Utilization、Saturation、Errors)检查 CPU、内存、磁盘、网络等资源。这个案例说明通用排障应先用对照和分阶段计时缩小范围,而不是看到某项资源有余量就结束调查。
使用 USE/RED 建立从服务到主机的证据链。
边界是观测本身会有成本:生产上限制采样频率、时长与范围;先保存时间、版本、部署和流量变化,确保证据能与事件关联。长期从数据库安全并发反推全服务连接预算,扩缩容同步调整池大小,并把池等待、总会话和业务尾延迟串成告警。
区分宿主机指标与 cgroup 内看到的有效资源。
随后从资源症状下钻到调用路径:确认容器 cgroup 限额,比较应用延迟、调度等待、块 I/O 延迟和网络 RTT;用 /proc/<pid>、strace -ttT -p 做短时采样,必要时用 perf 或 eBPF 聚合栈。这个案例说明通用排障应先用对照和分阶段计时缩小范围,而不是看到某项资源有余量就结束调查。
采样工具分级使用,控制生产观测开销。
边界是观测本身会有成本:生产上限制采样频率、时长与范围;先保存时间、版本、部署和流量变化,确保证据能与事件关联。这个案例说明通用排障应先用对照和分阶段计时缩小范围,而不是看到某项资源有余量就结束调查。
Linux load average 与 CPU 使用率分别说明什么?¶
技术说明
Linux load average 是 1、5、15 分钟指数衰减的平均活动任务数,包含可运行状态 R 以及不可中断睡眠 D 的任务,并不等于 CPU 百分比。判断是否异常要用 load/可用 CPU 数量并观察趋势,同时用 vmstat 的 r、ps -eo state,wchan,comm 区分 CPU 竞争和 I/O/内核等待。容器里 /proc/loadavg 往往反映宿主机视角,必须配合 cgroup 指标。
CPU 使用率由 user、system、idle、iowait、steal 等时间构成。iowait 是某 CPU 空闲且系统存在待完成 I/O 的时间,不能直接归因某块盘;steal 在虚拟机中提示 vCPU 被宿主机拿走。高 load 低 CPU 常见于大量 D 状态、锁或配额限流;低 load 高 CPU 则可能是少量线程打满少数核。mpstat -P ALL 1 可揭示单核热点,pidstat -w 可看调度切换。
实践经验
复合场景中,节点 load 从 8 升至 90,而 CPU idle 仍有 60%。ps 显示备份进程及业务线程大量处于 D,wchan 落在 NFS I/O,客户端重传同步升高。团队暂停备份、把读流量切到健康副本,并对挂载设置合理超时;强制卸载可能造成写入风险,因此未盲目执行。最终隔离备份网络,加入 NFS 延迟、D 状态数量和任务运行窗口告警。
面试回答要点
load 是活动任务队列,不是 CPU 利用率。
Linux load average 是 1、5、15 分钟指数衰减的平均活动任务数,包含可运行状态 R 以及不可中断睡眠 D 的任务,并不等于 CPU 百分比。复合场景中,节点 load 从 8 升至 90,而 CPU idle 仍有 60%。
R 与 D 要分别查,不能只按核数套阈值。
Linux load average 是 1、5、15 分钟指数衰减的平均活动任务数,包含可运行状态 R 以及不可中断睡眠 D 的任务,并不等于 CPU 百分比。最终隔离备份网络,加入 NFS 延迟、D 状态数量和任务运行窗口告警。
看单核、iowait、steal 及 cgroup 视角。
CPU 使用率由 user、system、idle、iowait、steal 等时间构成。ps 显示备份进程及业务线程大量处于 D,wchan 落在 NFS I/O,客户端重传同步升高。
用业务基线解释绝对值,并关联时间线。
CPU 使用率由 user、system、idle、iowait、steal 等时间构成。团队暂停备份、把读流量切到健康副本,并对挂载设置合理超时;强制卸载可能造成写入风险,因此未盲目执行。
如何诊断 CPU 饱和、调度延迟与 cgroup CPU 限流?¶
技术说明
CPU 饱和表现为每核 busy 高、运行队列持续超过可用核数、调度延迟和业务尾延迟上升。先用 mpstat -P ALL 1 区分 user/system/softirq/steal,用 pidstat -u -w -t 1 找到线程和切换,再用 perf top、火焰图分析热点。高 system 可能来自系统调用、网络软中断或锁;频繁自愿/非自愿切换需结合线程模型判断,不能简单认为越多越坏。
cgroup v2 的 cpu.max 定义 quota/period,达到配额后任务会在本周期剩余时间被限流;cpu.stat 中 nr_periods、nr_throttled、throttled_usec 是关键证据。突发并行工作即使平均 CPU 不高也会产生周期性长尾。CPU weight 只在争用时决定相对份额,不是硬上限;修改 quota 前还要核实节点余量、编排器请求值和 QoS。
实践经验
复合场景中,网络代理在高包速率时 P99 突增,整机 CPU 平均仅 55%。mpstat -P ALL 显示 CPU0 的 softirq 接近满载,/proc/interrupts 与网卡队列统计又证明多数 RX 中断集中在同一核;cpu.stat 没有限流增量,steal 也稳定。团队先降低非关键小包流量并把部分入口迁走,随后在同一 NUMA 节点内调整 RSS/IRQ 队列分布。重新分布中断会改变缓存局部性并可能造成乱序,因此按单队列金丝雀,联合验证丢包、吞吐和尾延迟,而不是只追求各核利用率平均。
面试回答要点
分清热点计算、调度竞争、虚拟化 steal 和 quota 限流。
CPU weight 只在争用时决定相对份额,不是硬上限;修改 quota 前还要核实节点余量、编排器请求值和 QoS。mpstat -P ALL 显示 CPU0 的 softirq 接近满载,/proc/interrupts 与网卡队列统计又证明多数 RX 中断集中在同一核;cpu.stat 没有限流增量,steal 也稳定。
读取 cpu.stat,不以平均 CPU 排除限流。
突发并行工作即使平均 CPU 不高也会产生周期性长尾。mpstat -P ALL 显示 CPU0 的 softirq 接近满载,/proc/interrupts 与网卡队列统计又证明多数 RX 中断集中在同一核;cpu.stat 没有限流增量,steal 也稳定。
优先减少无效并行或水平扩展,再审慎调限额。
CPU 饱和表现为每核 busy 高、运行队列持续超过可用核数、调度延迟和业务尾延迟上升。团队先降低非关键小包流量并把部分入口迁走,随后在同一 NUMA 节点内调整 RSS/IRQ 队列分布。
修复后验证吞吐、P99、成本与下游连接压力。
cgroup v2 的 cpu.max 定义 quota/period,达到配额后任务会在本周期剩余时间被限流;cpu.stat 中 nr_periods、nr_throttled、throttled_usec 是关键证据。重新分布中断会改变缓存局部性并可能造成乱序,因此按单队列金丝雀,联合验证丢包、吞吐和尾延迟,而不是只追求各核利用率平均。
free、available、RSS、page cache 应如何解释?¶
技术说明
Linux 会把空闲内存用于页缓存,因此 free 很低不等于内存不足。/proc/meminfo 的 MemAvailable 估算无需交换即可供新负载使用的内存;Cached、Buffers、slab 中可回收部分需结合观察。进程 RSS 是驻留物理页总量,包含共享页,直接相加会重复;PSS 按共享比例分摊,更适合归因,可从 /proc/<pid>/smaps_rollup 或 smem 获取。
匿名内存、文件页、共享内存和内核内存受不同回收路径影响。判断泄漏应看稳定流量下的长期 PSS/heap 增长、回收后是否下降,而非一次 RSS 峰值。容器需要读 memory.current、memory.stat、memory.events 和 memory.max;节点有可用内存并不代表容器不会因硬上限 OOM。页缓存被计入 cgroup 的具体归属还与首次装入和内核版本行为相关。
实践经验
复合场景中,团队看到 free 只剩 2%便计划重启数据库,但 MemAvailable 尚有 35%,大部分是可回收文件页。真正异常的是 sidecar 的匿名 PSS 单调增长,且 memory.events 的 high 持续增加。先限制 sidecar 队列和采样率止血,接受短期日志丢弃;长期修复缓存无界增长、设置内存高水位与丢弃策略,并用 PSS、匿名页、OOM 事件而非 free 百分比告警。
面试回答要点
低 free 常是正常缓存行为,重点看 MemAvailable 与压力。
页缓存被计入 cgroup 的具体归属还与首次装入和内核版本行为相关。复合场景中,团队看到 free 只剩 2%便计划重启数据库,但 MemAvailable 尚有 35%,大部分是可回收文件页。
RSS 不适合跨进程简单求和,归因优先 PSS。
进程 RSS 是驻留物理页总量,包含共享页,直接相加会重复;PSS 按共享比例分摊,更适合归因,可从 /proc/<pid>/smaps_rollup 或 smem 获取。先限制 sidecar 队列和采样率止血,接受短期日志丢弃;长期修复缓存无界增长、设置内存高水位与丢弃策略,并用 PSS、匿名页、OOM 事件而非 free 百分比告警。
区分匿名页、文件页、slab 与可回收性。
匿名内存、文件页、共享内存和内核内存受不同回收路径影响。复合场景中,团队看到 free 只剩 2%便计划重启数据库,但 MemAvailable 尚有 35%,大部分是可回收文件页。
容器上限、节点余量和业务内存曲线必须同时看。
容器需要读 memory.current、memory.stat、memory.events 和 memory.max;节点有可用内存并不代表容器不会因硬上限 OOM。先限制 sidecar 队列和采样率止血,接受短期日志丢弃;长期修复缓存无界增长、设置内存高水位与丢弃策略,并用 PSS、匿名页、OOM 事件而非 free 百分比告警。
Linux OOM 发生时如何确认牺牲者、根因与恢复策略?¶
技术说明
OOM 可能发生于系统全局、NUMA 节点或某个 memory cgroup。内核依据可释放内存与约束选择进程,综合 oom_score、内存占用和 oom_score_adj;容器中的 cgroup OOM 常只杀组内任务。检查内核日志中的 oom-kill/Killed process、cgroup v2 的 memory.events{,.local} 中 oom、oom_kill,并对齐 Kubernetes 退出码与 OOMKilled。仅看到退出码 137 不能断言 OOM,也可能是外部 SIGKILL。
根因分析要重建事发前匿名页、page cache、slab、swap、工作集和请求并发;确认 memory.max、memory.high、swap 限制及 QoS。memory.high 可用于节流和回收反馈,memory.max 是最终硬边界。不要靠禁用 OOM killer 或无限加内存掩盖问题;关键进程可调整保护,但过度负值会让系统杀掉更不合适的任务,甚至造成整机失联。
实践经验
复合场景中,图片处理 Pod 在活动流量突增时反复 137。节点仍有 20GiB 可用,但 memory.events 的 oom_kill 增长,峰值并发乘以单任务解码内存超过容器 memory.max。团队先限流并降低 worker 数,代价是队列延迟上升;随后流式解码、按输入尺寸设成本权重、用 VPA 建议校准 request,并对 OOM 事件与队列年龄联合告警,故障演练验证不会形成重启风暴。
面试回答要点
先区分全局 OOM、cgroup OOM 与普通 SIGKILL。
OOM 可能发生于系统全局、NUMA 节点或某个 memory cgroup。团队先限流并降低 worker 数,代价是队列延迟上升;随后流式解码、按输入尺寸设成本权重、用 VPA 建议校准 request,并对 OOM 事件与队列年龄联合告警,故障演练验证不会形成重启风暴。
用内核日志、memory.events 和编排事件交叉验证。
检查内核日志中的 oom-kill/Killed process、cgroup v2 的 memory.events{,.local} 中 oom、oom_kill,并对齐 Kubernetes 退出码与 OOMKilled。团队先限流并降低 worker 数,代价是队列延迟上升;随后流式解码、按输入尺寸设成本权重、用 VPA 建议校准 request,并对 OOM 事件与队列年龄联合告警,故障演练验证不会形成重启风暴。
用并发×单任务峰值重建容量,而非只看平均值。
根因分析要重建事发前匿名页、page cache、slab、swap、工作集和请求并发;确认 memory.max、memory.high、swap 限制及 QoS。节点仍有 20GiB 可用,但 memory.events 的 oom_kill 增长,峰值并发乘以单任务解码内存超过容器 memory.max。
止血要考虑限流导致的排队和重试副作用。
根因分析要重建事发前匿名页、page cache、slab、swap、工作集和请求并发;确认 memory.max、memory.high、swap 限制及 QoS。团队先限流并降低 worker 数,代价是队列延迟上升;随后流式解码、按输入尺寸设成本权重、用 VPA 建议校准 request,并对 OOM 事件与队列年龄联合告警,故障演练验证不会形成重启风暴。
如何分析缺页、swap 抖动与内存回收造成的长尾?¶
技术说明
缺页分 minor 与 major:minor fault 通常只需建立页表映射,major fault 需要从存储取页,成本差异巨大。pidstat -r 1 的 minflt/s、majflt/s,vmstat 1 的 si/so,以及 /proc/vmstat 的 pgscan、pgsteal 可观察回收活动。单次 major fault 并非一定异常,例如冷启动映射文件;问题在于它是否持续、是否与业务尾延迟和存储等待重合。
swap 不是天然错误,它能把长期不用的匿名页换出,为文件缓存腾空间;但工作集超过内存时反复换入换出会形成 thrashing。PSI memory 的 some/full 能反映任务因回收或换页停顿的时间比例,比单看 swap 使用量更接近影响。调 vm.swappiness、关闭 swap 或清缓存都不是通用答案,应先识别工作集、脏页写回、NUMA 和 cgroup 回收边界。
实践经验
复合场景中,一台批处理节点每晚 P99 抖动,swap 已占用 40%,但真正的转折是 pswpin、major fault 与 memory PSI full 同时上升。新任务把活跃工作集推过物理内存,旧进程被换出后又被频繁访问。团队暂停低优先级任务、降低并发止血;直接 swapoff 会瞬间需要大量内存,故未执行。长期按峰值工作集分池、设置 cgroup memory.high,并以 PSI 和队列延迟驱动调度。
面试回答要点
区分 minor/major fault、已用 swap 与正在换页。
PSI memory 的 some/full 能反映任务因回收或换页停顿的时间比例,比单看 swap 使用量更接近影响。复合场景中,一台批处理节点每晚 P99 抖动,swap 已占用 40%,但真正的转折是 pswpin、major fault 与 memory PSI full 同时上升。
用 PSI 判断回收停顿是否真的影响任务。
PSI memory 的 some/full 能反映任务因回收或换页停顿的时间比例,比单看 swap 使用量更接近影响。团队暂停低优先级任务、降低并发止血;直接 swapoff 会瞬间需要大量内存,故未执行。
避免在内存紧张时贸然 swapoff 或清缓存。
swap 不是天然错误,它能把长期不用的匿名页换出,为文件缓存腾空间;但工作集超过内存时反复换入换出会形成 thrashing。团队暂停低优先级任务、降低并发止血;直接 swapoff 会瞬间需要大量内存,故未执行。
按活跃工作集和并发做容量与隔离。
调 vm.swappiness、关闭 swap 或清缓存都不是通用答案,应先识别工作集、脏页写回、NUMA 和 cgroup 回收边界。新任务把活跃工作集推过物理内存,旧进程被换出后又被频繁访问。
文件描述符耗尽如何定位,为什么只调大 ulimit 不够?¶
技术说明
文件描述符是进程对 socket、管道、普通文件、eventfd 等打开对象的索引。进程软硬上限可从 /proc/<pid>/limits 查看,systemd 服务还受 LimitNOFILE= 控制;系统级 fs.file-max 与 /proc/sys/fs/file-nr 描述更大范围的文件句柄容量。EMFILE 表示进程上限,ENFILE 表示系统级不足,两者处置不同。ls /proc/<pid>/fd | wc -l 可计数,lsof -p 或按链接类型聚合可归因。
持续增长通常是连接、文件或管道未关闭,也可能是合理的连接池扩大。调大限制只能延后故障,还会增加内核对象内存,并可能把压力转移到下游连接上限。诊断要看 fd 数量斜率、类型、建立/关闭速率、TCP 状态和应用池配置;高并发服务还要核对 accept backlog、端口范围及 conntrack,而不是把所有“连接失败”归为 fd。
实践经验
复合场景中,代理进程逐步出现 Too many open files,重启可恢复数小时。fd 聚合显示指向已删除临时文件的句柄持续增长,代码在错误分支漏掉 close。团队轮转实例止血并临时提高软限制,以换取修复窗口;副作用是泄漏占用磁盘继续累积。长期使用作用域资源管理修复代码,压测错误路径,告警 fd/limit 比率和增长率,并在发布前验证长期稳态而非短压测。
面试回答要点
区分进程 EMFILE 与系统 ENFILE。
EMFILE 表示进程上限,ENFILE 表示系统级不足,两者处置不同。复合场景中,代理进程逐步出现 Too many open files,重启可恢复数小时。
按 fd 类型和增长率找泄漏,不只看总数。
诊断要看 fd 数量斜率、类型、建立/关闭速率、TCP 状态和应用池配置;高并发服务还要核对 accept backlog、端口范围及 conntrack,而不是把所有“连接失败”归为 fd。fd 聚合显示指向已删除临时文件的句柄持续增长,代码在错误分支漏掉 close。
调限额是缓解措施,需评估内存及下游压力。
调大限制只能延后故障,还会增加内核对象内存,并可能把压力转移到下游连接上限。团队轮转实例止血并临时提高软限制,以换取修复窗口;副作用是泄漏占用磁盘继续累积。
覆盖异常路径、长稳压测和 fd 比率告警。
诊断要看 fd 数量斜率、类型、建立/关闭速率、TCP 状态和应用池配置;高并发服务还要核对 accept backlog、端口范围及 conntrack,而不是把所有“连接失败”归为 fd。长期使用作用域资源管理修复代码,压测错误路径,告警 fd/limit 比率和增长率,并在发布前验证长期稳态而非短压测。
磁盘容量充足却无法写入,如何检查 inode、配额与保留块?¶
技术说明
文件系统有字节容量和 inode 数量两种独立资源。df -hT 看块空间,df -ih 看 inode;海量小文件可能先耗尽 inode,表现为 ENOSPC。此外还要检查用户/项目配额、只读重新挂载、ext4 保留块、LVM/薄池元数据、XFS allocation group,以及容器 overlay 层容量。du 与 df 不一致可能源于已删除但仍打开的文件、挂载点遮蔽或文件系统元数据。
定位目录时可按层级计数和统计尺寸,但在数百万文件的生产盘上全量 find 会产生大量 I/O,应限定目录、深度和优先级。lsof +L1 查链接数为零仍被占用的文件;配额用文件系统对应工具核实。清理必须理解文件用途及写入方,直接删除活跃数据库或容器运行时目录可能造成不可恢复损坏。
实践经验
复合场景中,日志分区显示 48% 空闲却创建文件失败,df -i 显示 inode 100%。某调试开关按请求生成零字节追踪文件。团队先关闭开关、把旧目录移动到同文件系统隔离后分批删除,避免一次删除导致 I/O 风暴;随后将追踪改为批量对象存储、设置文件数配额和 inode 使用率告警,并演练“磁盘满”时日志组件的降级行为。
面试回答要点
同时查块、inode、配额、只读状态和薄池元数据。
此外还要检查用户/项目配额、只读重新挂载、ext4 保留块、LVM/薄池元数据、XFS allocation group,以及容器 overlay 层容量。团队先关闭开关、把旧目录移动到同文件系统隔离后分批删除,避免一次删除导致 I/O 风暴;随后将追踪改为批量对象存储、设置文件数配额和 inode 使用率告警,并演练“磁盘满”时日志组件的降级行为。
用 lsof +L1 解释常见的 du/df 差异。
lsof +L1 查链接数为零仍被占用的文件;配额用文件系统对应工具核实。团队先关闭开关、把旧目录移动到同文件系统隔离后分批删除,避免一次删除导致 I/O 风暴;随后将追踪改为批量对象存储、设置文件数配额和 inode 使用率告警,并演练“磁盘满”时日志组件的降级行为。
大目录扫描与删除要限速,避免放大 I/O。
定位目录时可按层级计数和统计尺寸,但在数百万文件的生产盘上全量 find 会产生大量 I/O,应限定目录、深度和优先级。团队先关闭开关、把旧目录移动到同文件系统隔离后分批删除,避免一次删除导致 I/O 风暴;随后将追踪改为批量对象存储、设置文件数配额和 inode 使用率告警,并演练“磁盘满”时日志组件的降级行为。
从写入生命周期上消除小文件和无界保留。
清理必须理解文件用途及写入方,直接删除活跃数据库或容器运行时目录可能造成不可恢复损坏。团队先关闭开关、把旧目录移动到同文件系统隔离后分批删除,避免一次删除导致 I/O 风暴;随后将追踪改为批量对象存储、设置文件数配额和 inode 使用率告警,并演练“磁盘满”时日志组件的降级行为。
如何解读 iostat,并从设备延迟追到应用 I/O?¶
技术说明
iostat -xz 1 常看吞吐、IOPS、await、平均队列长度与 %util。await 是排队加服务时间的平均值,会掩盖尾延迟;%util 表示采样期间设备至少有 I/O 的时间,对可并行的 NVMe、RAID 或虚拟设备不能简单解释为“100%才饱和”。请求合并、块大小、读写比例和队列深度都会影响指标,必须与设备能力基线比较。
从设备回到进程可用 pidstat -d 1、cgroup I/O 统计和 /proc/<pid>/io,再结合应用层 fsync、数据库 WAL、缓存命中及延迟直方图。页缓存使应用 write 很快但脏页稍后集中写回;vmstat、/proc/vmstat 与 writeback 指标能确认。云盘还可能受 IOPS/吞吐额度、突发积分和宿主机限速影响,应查提供方指标而非只看 guest。
实践经验
复合场景中,数据库提交延迟周期性升高,%util 仅 65%,但 await 和云盘 burst balance 同步恶化,WAL fsync P99 飙升。团队把非关键分析查询迁走并临时扩容盘性能止血;提高性能带来直接成本。长期将 WAL 与数据盘隔离、按峰值 IOPS/吞吐双维度建模,告警使用延迟分位数和额度余量,并用故障注入验证应用超时与降级。
面试回答要点
await 是排队与服务时间,平均值不能代表尾部。
await 是排队加服务时间的平均值,会掩盖尾延迟;%util 表示采样期间设备至少有 I/O 的时间,对可并行的 NVMe、RAID 或虚拟设备不能简单解释为“100%才饱和”。长期将 WAL 与数据盘隔离、按峰值 IOPS/吞吐双维度建模,告警使用延迟分位数和额度余量,并用故障注入验证应用超时与降级。
并行设备上 %util 不能直接等同容量百分比。
await 是排队加服务时间的平均值,会掩盖尾延迟;%util 表示采样期间设备至少有 I/O 的时间,对可并行的 NVMe、RAID 或虚拟设备不能简单解释为“100%才饱和”。团队把非关键分析查询迁走并临时扩容盘性能止血;提高性能带来直接成本。
串联进程、cgroup、页缓存、文件系统和云盘指标。
从设备回到进程可用 pidstat -d 1、cgroup I/O 统计和 /proc/<pid>/io,再结合应用层 fsync、数据库 WAL、缓存命中及延迟直方图。复合场景中,数据库提交延迟周期性升高,%util 仅 65%,但 await 和云盘 burst balance 同步恶化,WAL fsync P99 飙升。
容量同时考虑 IOPS、吞吐、块大小与突发额度。
云盘还可能受 IOPS/吞吐额度、突发积分和宿主机限速影响,应查提供方指标而非只看 guest。长期将 WAL 与数据盘隔离、按峰值 IOPS/吞吐双维度建模,告警使用延迟分位数和额度余量,并用故障注入验证应用超时与降级。
ext4/XFS 日志机制能保证什么,不能保证什么?¶
技术说明
日志型文件系统主要保护元数据一致性,使崩溃恢复时可重放或回滚未完成事务;它不自动保证应用最近写入的数据已经持久化。ext4 默认常见的 ordered 模式会在相关元数据提交前安排数据块写出,但应用仍需正确使用 fsync/fdatasync,重命名替换文件时通常还需同步父目录。磁盘写缓存、控制器屏障和虚拟化层也必须正确兑现 flush 语义。
XFS 采用元数据日志并面向并行与大文件工作负载,扩缩、修复工具及行为与 ext4 不同,不能混用经验。选择文件系统需基于文件数量、I/O 模式、快照/配额需求和运维能力;mount 参数如 noatime、提交周期可能改善性能,却改变故障窗口。数据库的 WAL 并不会让底层错误持久化语义变得无关,仍需验证 fsync 和电源故障模型。
实践经验
复合场景中,配置服务用“写临时文件后 rename”发布配置,断电演练后偶尔出现零长度目标文件。代码没有同步临时文件及目录,只依赖文件系统日志。团队先停用本地写入并从远端版本库恢复;随后实现 write→fsync(file)→rename→fsync(directory),加入校验和与版本回退。代价是发布延迟略增,因此采用批量合并而非取消持久化保证。
面试回答要点
日志主要保证文件系统一致性,不等于应用数据必然持久。
日志型文件系统主要保护元数据一致性,使崩溃恢复时可重放或回滚未完成事务;它不自动保证应用最近写入的数据已经持久化。代码没有同步临时文件及目录,只依赖文件系统日志。
原子 rename 与持久 rename 是两个概念。
日志型文件系统主要保护元数据一致性,使崩溃恢复时可重放或回滚未完成事务;它不自动保证应用最近写入的数据已经持久化。代价是发布延迟略增,因此采用批量合并而非取消持久化保证。
校验 fsync、目录同步、flush 和硬件写缓存链路。
ext4 默认常见的 ordered 模式会在相关元数据提交前安排数据块写出,但应用仍需正确使用 fsync/fdatasync,重命名替换文件时通常还需同步父目录。团队先停用本地写入并从远端版本库恢复;随后实现 write→fsync(file)→rename→fsync(directory),加入校验和与版本回退。
文件系统选型与参数必须通过真实故障模型验证。
选择文件系统需基于文件数量、I/O 模式、快照/配额需求和运维能力;mount 参数如 noatime、提交周期可能改善性能,却改变故障窗口。代码没有同步临时文件及目录,只依赖文件系统日志。
为什么删除日志后磁盘空间不释放,怎样安全处理?¶
技术说明
Unix 文件是 inode 与目录项的组合。unlink 只删除目录项;只要进程仍持有打开的文件描述符,inode 和数据块就不会释放。此时 du 因路径消失而看不到空间,df 仍显示被占用。可用 lsof +L1 或检查 /proc/*/fd 中标记 (deleted) 的链接,确认占用进程、fd、文件大小和挂载点。
最安全的处置通常是让写入程序重新打开文件,例如正确发送它支持的 reopen 信号、滚动重启实例,或使用日志框架的轮转接口。对 fd 执行截断虽能释放空间,但可能破坏应用偏移、稀疏文件和审计完整性,只应在明确文件语义、完成备份且紧急时使用。logrotate 的 copytruncate 存在复制与截断之间的丢失窗口,优先 rename 后通知应用 reopen。
实践经验
复合场景中,运维删除了 80GiB 日志但分区仍 96%。lsof +L1 定位旧 Java 进程持有已删除文件。团队先把流量移走,再滚动重启释放 fd,避免直接杀全组导致容量骤降;长期将应用日志输出到 stdout/受管代理,轮转时显式 reopen,监控“deleted-open bytes”。同时限制日志速率,接受过载时采样丢弃的可见性副作用。
面试回答要点
用 inode 引用计数解释 du 与 df 差异。
此时 du 因路径消失而看不到空间,df 仍显示被占用。团队先把流量移走,再滚动重启释放 fd,避免直接杀全组导致容量骤降;长期将应用日志输出到 stdout/受管代理,轮转时显式 reopen,监控“deleted-open bytes”。
先定位持有 fd 的进程和文件用途。
unlink 只删除目录项;只要进程仍持有打开的文件描述符,inode 和数据块就不会释放。lsof +L1 定位旧 Java 进程持有已删除文件。
优先应用 reopen 或滚动重启,慎用直接截断。
最安全的处置通常是让写入程序重新打开文件,例如正确发送它支持的 reopen 信号、滚动重启实例,或使用日志框架的轮转接口。团队先把流量移走,再滚动重启释放 fd,避免直接杀全组导致容量骤降;长期将应用日志输出到 stdout/受管代理,轮转时显式 reopen,监控“deleted-open bytes”。
修复轮转协议并监控 deleted-open 空间。
对 fd 执行截断虽能释放空间,但可能破坏应用偏移、稀疏文件和审计完整性,只应在明确文件语义、完成备份且紧急时使用。团队先把流量移走,再滚动重启释放 fd,避免直接杀全组导致容量骤降;长期将应用日志输出到 stdout/受管代理,轮转时显式 reopen,监控“deleted-open bytes”。
如何处理僵尸进程与长期 D 状态进程?¶
技术说明
僵尸进程已退出,仅保留退出码等少量进程表信息,等待父进程 wait() 回收;它不消耗 CPU 或普通地址空间。ps -eo pid,ppid,state,cmd 可查 Z,真正应修复的是父进程未回收子进程。杀僵尸本身无效,需让父进程处理 SIGCHLD、重启父进程,或使其退出后由可靠的 subreaper/PID 1 接管。容器入口进程尤其需要正确 reap。
D 是不可中断睡眠,多数在内核等待 I/O 或锁,以避免关键操作被信号打断。SIGKILL 通常要等内核路径返回才生效;wchan、内核栈 /proc/<pid>/stack、块设备/NFS 指标和 sysrq task dump 可定位等待点。不要把 Z 与 D 混为一谈:大量 Z 可能耗尽 PID,长期 D 则会抬高 load 并常提示存储、驱动或网络文件系统故障。
实践经验
复合场景中,采集器容器数小时后无法 fork,进程表有数万个 Z,父进程是未实现 wait 的 shell wrapper。团队滚动替换容器并临时降低任务生成速率;长期改用具备信号转发与回收能力的 init,增加 PID cgroup 限制和僵尸数告警。另一批 D 状态来自失联 NFS,处置是隔离挂载和恢复服务端,而非反复 kill -9。
面试回答要点
Z 要修父进程回收逻辑,杀 Z 本身没有意义。
ps -eo pid,ppid,state,cmd 可查 Z,真正应修复的是父进程未回收子进程。复合场景中,采集器容器数小时后无法 fork,进程表有数万个 Z,父进程是未实现 wait 的 shell wrapper。
D 要从 wchan/内核栈追到 I/O 或内核等待。
D 是不可中断睡眠,多数在内核等待 I/O 或锁,以避免关键操作被信号打断。另一批 D 状态来自失联 NFS,处置是隔离挂载和恢复服务端,而非反复 kill -9。
容器 PID 1 需负责信号转发和子进程回收。
ps -eo pid,ppid,state,cmd 可查 Z,真正应修复的是父进程未回收子进程。团队滚动替换容器并临时降低任务生成速率;长期改用具备信号转发与回收能力的 init,增加 PID cgroup 限制和僵尸数告警。
分别监控 PID 数、D 状态、load 与依赖延迟。
不要把 Z 与 D 混为一谈:大量 Z 可能耗尽 PID,长期 D 则会抬高 load 并常提示存储、驱动或网络文件系统故障。另一批 D 状态来自失联 NFS,处置是隔离挂载和恢复服务端,而非反复 kill -9。
Linux namespace 提供哪些隔离,边界在哪里?¶
技术说明
常见 namespace 包括 mount、PID、network、UTS、IPC、user、cgroup 和 time;它们让进程看到不同的挂载树、进程号、网络栈、主机名、IPC 对象、用户映射等。namespace 主要隔离“视图”,不等于资源限额,CPU、内存和 I/O 公平性依赖 cgroup。lsns、nsenter 及 /proc/<pid>/ns/* 可检查归属,但进入生产 namespace 等同较强诊断权限,应审计。
容器共享同一宿主机内核,内核漏洞、过宽 capability、危险挂载或设备访问可能突破预期边界。user namespace 可把容器内 root 映射成宿主机非特权 UID,降低风险,但与文件权限、存储插件存在兼容性要求。安全设计还需 seccomp、LSM(SELinux/AppArmor)、只读根文件系统、最小 capability 和运行时隔离;不能把“在容器里”当安全控制。
实践经验
复合场景中,排障 Pod 被授予 hostPID、hostNetwork、特权模式和宿主机根目录挂载,攻击面等同节点管理员。团队先移除常驻 Pod、改为审批后短时创建,并记录 nsenter 操作;长期建立分级诊断镜像、只授必要 capability、启用准入策略与审计。副作用是紧急排障多一步授权,因此预先演练并保留 break-glass 流程。
面试回答要点
namespace 隔离视图,cgroup 管资源,两者职责不同。
namespace 主要隔离“视图”,不等于资源限额,CPU、内存和 I/O 公平性依赖 cgroup。复合场景中,排障 Pod 被授予 hostPID、hostNetwork、特权模式和宿主机根目录挂载,攻击面等同节点管理员。
容器共享内核,不是虚拟机级天然边界。
容器共享同一宿主机内核,内核漏洞、过宽 capability、危险挂载或设备访问可能突破预期边界。复合场景中,排障 Pod 被授予 hostPID、hostNetwork、特权模式和宿主机根目录挂载,攻击面等同节点管理员。
capability、挂载、设备与 host namespace 是关键风险。
容器共享同一宿主机内核,内核漏洞、过宽 capability、危险挂载或设备访问可能突破预期边界。团队先移除常驻 Pod、改为审批后短时创建,并记录 nsenter 操作;长期建立分级诊断镜像、只授必要 capability、启用准入策略与审计。
诊断权限短时化、可审计,并配套应急流程。
lsns、nsenter 及 /proc/<pid>/ns/* 可检查归属,但进入生产 namespace 等同较强诊断权限,应审计。团队先移除常驻 Pod、改为审批后短时创建,并记录 nsenter 操作;长期建立分级诊断镜像、只授必要 capability、启用准入策略与审计。
cgroup v2 与 v1 的关键差异和生产使用要点是什么?¶
技术说明
cgroup v2 使用统一层级,控制器在同一棵树协同,采用更一致的文件接口和“进程不能同时处于内部节点与有控制器子节点”等约束。常用接口包括 cpu.max/cpu.weight、memory.current/memory.high/memory.max、io.max/io.weight、pids.max 和 cgroup.events。cgroup.controllers 与 cgroup.subtree_control 决定子树可用控制器;实际层级常由 systemd 管理,手工移动进程可能与服务管理冲突。
内存控制中 memory.high 是可恢复的节流边界,memory.max 是硬上限;CPU weight 是争用时的相对权重,CPU max 才是带周期的上限。v2 的 PSI 和本地事件便于观察压力,但不同内核、systemd 与容器运行时支持程度需核实。在 Kubernetes 中,memory.high 应由 kubelet 的 MemoryQoS 等受支持机制管理;截至 Kubernetes 1.36,该能力仍为默认关闭的 alpha 特性,不能由业务脚本直接改写 Pod cgroup。迁移不能只改路径:监控采集、语言运行时资源识别、编排 QoS 与节点参数都要回归。
实践经验
复合场景中,节点切换统一层级后,旧监控仍读取 v1 路径,仪表盘显示容器内存为零,容量保护失效。团队暂缓剩余节点升级、用节点级指标兜底;修复采集器后对 CPU 限流、OOM、PID 上限逐项做金丝雀验证。长期将 cgroup 模式纳入节点契约和升级测试,并禁止业务脚本直接修改 systemd 管理的层级。
面试回答要点
v2 是统一层级和统一语义,不只是目录改名。
cgroup v2 使用统一层级,控制器在同一棵树协同,采用更一致的文件接口和“进程不能同时处于内部节点与有控制器子节点”等约束。复合场景中,节点切换统一层级后,旧监控仍读取 v1 路径,仪表盘显示容器内存为零,容量保护失效。
解释 weight、high 与 max 的软硬边界。
内存控制中 memory.high 是可恢复的节流边界,memory.max 是硬上限;CPU weight 是争用时的相对权重,CPU max 才是带周期的上限。长期将 cgroup 模式纳入节点契约和升级测试,并禁止业务脚本直接修改 systemd 管理的层级。
由 systemd/运行时负责层级,避免手工争用。
cgroup.controllers 与 cgroup.subtree_control 决定子树可用控制器;实际层级常由 systemd 管理,手工移动进程可能与服务管理冲突。长期将 cgroup 模式纳入节点契约和升级测试,并禁止业务脚本直接修改 systemd 管理的层级。
迁移覆盖监控、语言运行时、QoS 与故障演练。
迁移不能只改路径:监控采集、语言运行时资源识别、编排 QoS 与节点参数都要回归。复合场景中,节点切换统一层级后,旧监控仍读取 v1 路径,仪表盘显示容器内存为零,容量保护失效。
PSI 指标如何用于容量管理和早期告警?¶
技术说明
Pressure Stall Information 统计任务因 CPU、内存或 I/O 资源不足而停顿的时间比例,位于 /proc/pressure/{cpu,memory,io},cgroup v2 也可提供局部视角。some 表示至少一个任务受阻;full 表示范围内所有非空闲任务同时受阻。系统级 CPU full 没有有效语义,Linux 5.13 起接口可能仍显示该行但固定为零以保持兼容;每个 cgroup 的 cpu.pressure 则可报告有意义的 full,读取时要区分作用域与内核版本。avg10/60/300 是不同窗口平均,total 是累计微秒。
阈值应从服务 SLO 与历史基线回归,而非全环境固定一个百分比。CPU some 可能来自配额或节点争用;memory full 常与直接回收、compact、swap 相关;I/O full 可能是所有可运行任务都在等待 I/O。Linux PSI 接口还支持触发器,但生产告警通常由采集系统计算持续时间,并和业务延迟、队列及 cgroup limit 联合判断。
实践经验
复合场景中,缓存节点 CPU 仅 50%、可用内存尚有 10%,但请求长尾先于 OOM 恶化。memory PSI some/full 提前十分钟上升,证据指向内存回收与压缩。团队先迁移部分分片、降低后台 compaction 并发止血;代价是磁盘占用短期增加。长期以 PSI burn rate 触发扩容,保留 OOM 为最后防线,并通过压力测试校准各机型阈值。
面试回答要点
PSI 衡量“因资源不足而停顿”,不是利用率替代品。
Pressure Stall Information 统计任务因 CPU、内存或 I/O 资源不足而停顿的时间比例,位于 /proc/pressure/{cpu,memory,io},cgroup v2 也可提供局部视角。团队先迁移部分分片、降低后台 compaction 并发止血;代价是磁盘占用短期增加。
理解 some/full,并区分系统级 CPU full 为零与 cgroup 级语义。
系统级 CPU full 没有有效语义,Linux 5.13 起接口可能仍显示该行但固定为零以保持兼容;每个 cgroup 的 cpu.pressure 则可报告有意义的 full,读取时要区分作用域与内核版本。memory PSI some/full 提前十分钟上升,证据指向内存回收与压缩。
优先使用 cgroup 局部视角并关联业务 SLO。
Pressure Stall Information 统计任务因 CPU、内存或 I/O 资源不足而停顿的时间比例,位于 /proc/pressure/{cpu,memory,io},cgroup v2 也可提供局部视角。复合场景中,缓存节点 CPU 仅 50%、可用内存尚有 10%,但请求长尾先于 OOM 恶化。
阈值靠基线和压测校准,避免跨负载硬套。
阈值应从服务 SLO 与历史基线回归,而非全环境固定一个百分比。长期以 PSI burn rate 触发扩容,保留 OOM 为最后防线,并通过压力测试校准各机型阈值。
systemd 服务为什么会“手工能启动、开机却失败”?¶
技术说明
systemd 以 unit 的依赖和事务模型启动服务,环境与交互 shell 不同:默认没有用户 profile、工作目录和完整 PATH。服务应使用绝对路径,通过 EnvironmentFile= 注入非秘密配置,明确 User=、Group=、WorkingDirectory=、RuntimeDirectory= 和权限。After= 只规定顺序,不会自动拉起依赖;需要 Wants=/Requires= 表达强弱依赖,等待“network-online”也不代表某个远端服务可用。
服务类型需匹配进程行为:Type=simple/exec/notify/forking 对就绪和失败判断不同。Restart=、StartLimitIntervalSec= 与 StartLimitBurst= 防止崩溃循环;TimeoutStopSec=、KillSignal= 配合优雅退出。排查用 systemctl status、journalctl -u、systemd-analyze critical-chain/verify,并检查沙箱选项如 ProtectSystem=、NoNewPrivileges= 是否阻断预期访问。
实践经验
复合场景中,代理升级后重启正常,但节点重启时服务失败。日志显示 DNS 尚未可用,程序启动即退出,随后触发 start limit。团队先 reset-failed 并在网络恢复后拉起;长期让程序对依赖采用有界退避而非启动即失败,unit 增加正确依赖与就绪通知,并用重启演练验收。无限重启可能形成依赖风暴,因此设置抖动和上限。
面试回答要点
systemd 环境不是登录 shell,路径和权限要显式化。
systemd 以 unit 的依赖和事务模型启动服务,环境与交互 shell 不同:默认没有用户 profile、工作目录和完整 PATH。无限重启可能形成依赖风暴,因此设置抖动和上限。
区分排序 After 与依赖 Wants/Requires。
After= 只规定顺序,不会自动拉起依赖;需要 Wants=/Requires= 表达强弱依赖,等待“network-online”也不代表某个远端服务可用。团队先 reset-failed 并在网络恢复后拉起;长期让程序对依赖采用有界退避而非启动即失败,unit 增加正确依赖与就绪通知,并用重启演练验收。
服务自己应能承受远端依赖暂不可用。
After= 只规定顺序,不会自动拉起依赖;需要 Wants=/Requires= 表达强弱依赖,等待“network-online”也不代表某个远端服务可用。无限重启可能形成依赖风暴,因此设置抖动和上限。
用就绪通知、退避和启动限速避免重启风暴。
systemd 以 unit 的依赖和事务模型启动服务,环境与交互 shell 不同:默认没有用户 profile、工作目录和完整 PATH。团队先 reset-failed 并在网络恢复后拉起;长期让程序对依赖采用有界退避而非启动即失败,unit 增加正确依赖与就绪通知,并用重启演练验收。
如何设计 Linux 服务的信号处理与优雅终止?¶
技术说明
SIGTERM 是可处理的终止请求,SIGKILL 不可捕获;常见编排流程先发 TERM,超过宽限期再 KILL。进程收到 TERM 后应停止接新流量、标记 not-ready、等待在途请求或提交点、刷新必要状态并关闭连接。顺序很关键:如果先断数据库池,仍在途请求会失败;如果一直保持就绪,负载均衡会在排空期间继续送流量。
PID 1 对默认信号行为与子进程回收有特殊责任。shell form 容器入口常让信号停在 shell,应用收不到;使用 exec form 或显式 exec,必要时加入轻量 init。宽限期要大于负载均衡传播延迟加最大可接受请求时间,但不能无限;长任务需要租约、检查点和幂等重放,而不是单纯拉长 termination grace。
实践经验
复合场景中,滚动发布每批都出现约 30 秒 502。证据显示 TERM 发给 wrapper,Java 未收到,直到 KILL 才断开数百连接。团队暂停发布、修正入口为 exec,并把 readiness 在排空开始时置失败;长期实现 pre-stop 排空和在途指标,压测滚动升级。副作用是副本在宽限期内占资源更久,因此容量需覆盖新旧版本重叠。
面试回答要点
TERM 触发排空,KILL 是最后期限而非正常路径。
SIGTERM 是可处理的终止请求,SIGKILL 不可捕获;常见编排流程先发 TERM,超过宽限期再 KILL。证据显示 TERM 发给 wrapper,Java 未收到,直到 KILL 才断开数百连接。
先摘流量,再完成在途工作,最后关闭依赖。
进程收到 TERM 后应停止接新流量、标记 not-ready、等待在途请求或提交点、刷新必要状态并关闭连接。团队暂停发布、修正入口为 exec,并把 readiness 在排空开始时置失败;长期实现 pre-stop 排空和在途指标,压测滚动升级。
确保 PID 1 转发信号并回收子进程。
PID 1 对默认信号行为与子进程回收有特殊责任。证据显示 TERM 发给 wrapper,Java 未收到,直到 KILL 才断开数百连接。
长任务用租约、检查点与幂等性解决。
宽限期要大于负载均衡传播延迟加最大可接受请求时间,但不能无限;长任务需要租约、检查点和幂等重放,而不是单纯拉长 termination grace。复合场景中,滚动发布每批都出现约 30 秒 502。
时间同步异常会造成哪些故障,如何设计监控与缓解?¶
技术说明
墙上时间受 NTP/PTP 校准,可能跳变;单调时钟只前进,应用计算超时和耗时应使用 monotonic clock。时钟偏差会影响 TLS 证书、OIDC/JWT 的 nbf/exp、分布式日志排序、租约、数据库复制和指标时间戳。NTP 通常通过渐进调速收敛,偏差过大或特定配置可能 step;timedatectl、chronyc tracking/sources 可检查同步、offset、stratum 和来源。
不能靠修改时区解决时钟偏差:系统内部统一 UTC,展示层转换时区。监控应覆盖 offset、同步状态、源数量和 leap 状态,并在关键系统设置失同步隔离策略。分布式一致性算法不应把普通墙钟当全序;租约要明确最大时钟漂移,追踪系统用 trace/span 关系与服务端校正辅助,而不能完全相信跨机时间差。
实践经验
复合场景中,一批节点因防火墙规则阻断时间源,偏差逐渐达到 7 分钟,OIDC token 被判“尚未生效”,发布任务大量失败。团队先摘除偏差节点、恢复受控时间源并谨慎渐进校时,避免时间跳变影响数据库;长期提供多源 NTP、对 offset 建快速告警和准入检查,并让认证错误面板显式区分时钟类失败。
面试回答要点
超时用单调时钟,业务时间才用墙钟。
墙上时间受 NTP/PTP 校准,可能跳变;单调时钟只前进,应用计算超时和耗时应使用 monotonic clock。复合场景中,一批节点因防火墙规则阻断时间源,偏差逐渐达到 7 分钟,OIDC token 被判“尚未生效”,发布任务大量失败。
时区、时钟偏差和同步状态是不同问题。
不能靠修改时区解决时钟偏差:系统内部统一 UTC,展示层转换时区。团队先摘除偏差节点、恢复受控时间源并谨慎渐进校时,避免时间跳变影响数据库;长期提供多源 NTP、对 offset 建快速告警和准入检查,并让认证错误面板显式区分时钟类失败。
关注认证、租约、日志与数据库的漂移边界。
时钟偏差会影响 TLS 证书、OIDC/JWT 的 nbf/exp、分布式日志排序、租约、数据库复制和指标时间戳。团队先摘除偏差节点、恢复受控时间源并谨慎渐进校时,避免时间跳变影响数据库;长期提供多源 NTP、对 offset 建快速告警和准入检查,并让认证错误面板显式区分时钟类失败。
多时间源、偏差告警和节点隔离要一起设计。
监控应覆盖 offset、同步状态、源数量和 leap 状态,并在关键系统设置失同步隔离策略。团队先摘除偏差节点、恢复受控时间源并谨慎渐进校时,避免时间跳变影响数据库;长期提供多源 NTP、对 offset 建快速告警和准入检查,并让认证错误面板显式区分时钟类失败。
NUMA 与透明大页为何可能影响数据库和低延迟服务?¶
技术说明
NUMA 系统中 CPU 访问本地内存通常比远端内存延迟低。进程线程、内存分配与中断分布不匹配时,会增加远端访问和互联带宽压力。用 numactl --hardware、numastat -p <pid>、lscpu 观察拓扑与远端页;优化应让 CPU 亲和性、内存策略和设备队列一致,而非盲目绑到单节点导致可用内存不足。
透明大页(THP)尝试把普通页合并成大页,能降低 TLB miss,但后台/直接 compaction 可能带来不可预测停顿。数据库常依据自身访问模式建议特定 THP 配置;显式 HugeTLB 则需预留并有不同管理语义。决策必须依据当前内核、数据库官方建议和基准测试,观察 AnonHugePages、THP split/collapse、memory PSI 和尾延迟,不能把禁用 THP 当通用模板。
实践经验
复合场景中,数据库迁移到双路大内存机后平均吞吐正常,P99 周期性恶化。numastat 显示远端访问偏高,compaction 指标与抖动重合。团队先限制实例跨 NUMA 漂移、降低并发止血;随后按数据库建议调整 THP 模式,绑定 CPU/内存并做故障容量测试。绑核降低了调度弹性,因此预留节点故障时的降级方案。
面试回答要点
先识别 NUMA 拓扑与远端内存证据。
用 numactl --hardware、numastat -p <pid>、lscpu 观察拓扑与远端页;优化应让 CPU 亲和性、内存策略和设备队列一致,而非盲目绑到单节点导致可用内存不足。团队先限制实例跨 NUMA 漂移、降低并发止血;随后按数据库建议调整 THP 模式,绑定 CPU/内存并做故障容量测试。
CPU、内存和设备亲和性应作为整体设计。
用 numactl --hardware、numastat -p <pid>、lscpu 观察拓扑与远端页;优化应让 CPU 亲和性、内存策略和设备队列一致,而非盲目绑到单节点导致可用内存不足。复合场景中,数据库迁移到双路大内存机后平均吞吐正常,P99 周期性恶化。
THP 有 TLB 收益,也可能有 compaction 长尾。
透明大页(THP)尝试把普通页合并成大页,能降低 TLB miss,但后台/直接 compaction 可能带来不可预测停顿。团队先限制实例跨 NUMA 漂移、降低并发止血;随后按数据库建议调整 THP 模式,绑定 CPU/内存并做故障容量测试。
遵循具体工作负载官方建议并以 P99 验证。
决策必须依据当前内核、数据库官方建议和基准测试,观察 AnonHugePages、THP split/collapse、memory PSI 和尾延迟,不能把禁用 THP 当通用模板。团队先限制实例跨 NUMA 漂移、降低并发止血;随后按数据库建议调整 THP 模式,绑定 CPU/内存并做故障容量测试。
如何从上下文切换和锁竞争定位多线程性能问题?¶
技术说明
上下文切换分自愿(等待锁、I/O、条件变量)和非自愿(时间片用尽或更高优先级抢占)。高切换率本身不是根因,事件驱动系统可能合理地频繁切换;要结合每次请求、CPU 周期、运行队列和调度延迟判断。pidstat -w -t 1、vmstat 1 可看切换,perf sched timehist 可分析 runnable 到运行的延迟。
锁竞争会表现为 futex 等待、热点自旋或内核锁等待。perf record -g、语言 profiler、eBPF off-CPU 分析可区分“在 CPU 上忙”和“等待”。修复可能包括缩小临界区、分片锁、无锁/读写结构、减少共享状态或调整线程数;但无锁结构复杂且易引入 ABA、内存序问题,线程更多也可能因缓存抖动与调度开销使吞吐下降。
实践经验
复合场景中,Go 服务加倍 worker 后吞吐反降,CPU system 与自愿切换升高。mutex profile 显示所有 worker 争用全局 LRU,off-CPU 栈集中在锁等待。团队先回退 worker 数止血;长期将缓存按租户分片、缩短锁内序列化,并用竞争检测与基准测试验证。分片提高命中不一致和内存占用,故增加总量上限与淘汰指标。
面试回答要点
切换率必须按请求和调度延迟解释。
高切换率本身不是根因,事件驱动系统可能合理地频繁切换;要结合每次请求、CPU 周期、运行队列和调度延迟判断。复合场景中,Go 服务加倍 worker 后吞吐反降,CPU system 与自愿切换升高。
on-CPU 与 off-CPU 两类分析相互补充。
perf record -g、语言 profiler、eBPF off-CPU 分析可区分“在 CPU 上忙”和“等待”。mutex profile 显示所有 worker 争用全局 LRU,off-CPU 栈集中在锁等待。
先找具体锁和临界区,再选分片或结构优化。
修复可能包括缩小临界区、分片锁、无锁/读写结构、减少共享状态或调整线程数;但无锁结构复杂且易引入 ABA、内存序问题,线程更多也可能因缓存抖动与调度开销使吞吐下降。团队先回退 worker 数止血;长期将缓存按租户分片、缩短锁内序列化,并用竞争检测与基准测试验证。
并发度、缓存局部性、内存与正确性要共同权衡。
修复可能包括缩小临界区、分片锁、无锁/读写结构、减少共享状态或调整线程数;但无锁结构复杂且易引入 ABA、内存序问题,线程更多也可能因缓存抖动与调度开销使吞吐下降。分片提高命中不一致和内存占用,故增加总量上限与淘汰指标。
eBPF 在生产观测中的价值、限制与安全边界是什么?¶
技术说明
eBPF 程序可在内核受控挂载点运行,通过 tracepoint、kprobe/kretprobe、uprobes、XDP、tc 等观测或处理事件,借助 map 向用户态聚合结果。验证器检查程序安全性和终止性,JIT 提升执行效率。生产诊断优先稳定 tracepoint 或 BTF/CO-RE,降低内核版本差异;动态 kprobe 对符号和实现更脆弱。常见用途包括 syscall 延迟、TCP 重传、调度/off-CPU、块 I/O 和按 cgroup 归因。
eBPF 不是“零开销”:高频事件逐条上报会造成 CPU、内存和 ring buffer 丢失,栈聚合也有限额。工具通常需要较高权限,错误的网络程序甚至会丢包;应限制 capability、签名和允许的程序类型,记录加载行为,并先在小范围测开销。数据解释也有边界,例如 kprobe 看到内核函数并不直接等于业务因果,需要与应用 trace 和时间线关联。
实践经验
复合场景中,服务偶发 200ms 抖动,传统平均指标无异常。短时 eBPF off-CPU 聚合发现线程阻塞在 DNS socket 接收,TCP 工具显示没有显著重传,最终定位到解析器串行等待。团队先扩容本地缓存并收紧单次解析超时;长期加入 DNS span 和缓存命中率。采样本身限定 60 秒与目标 cgroup,避免全节点高频追踪造成额外负载。
面试回答要点
说明挂载点、map、验证器与用户态数据路径。
eBPF 程序可在内核受控挂载点运行,通过 tracepoint、kprobe/kretprobe、uprobes、XDP、tc 等观测或处理事件,借助 map 向用户态聚合结果。采样本身限定 60 秒与目标 cgroup,避免全节点高频追踪造成额外负载。
稳定 tracepoint/BTF 通常比内核私有符号可靠。
生产诊断优先稳定 tracepoint 或 BTF/CO-RE,降低内核版本差异;动态 kprobe 对符号和实现更脆弱。短时 eBPF off-CPU 聚合发现线程阻塞在 DNS socket 接收,TCP 工具显示没有显著重传,最终定位到解析器串行等待。
控制频率、采样、缓冲丢失与权限风险。
工具通常需要较高权限,错误的网络程序甚至会丢包;应限制 capability、签名和允许的程序类型,记录加载行为,并先在小范围测开销。采样本身限定 60 秒与目标 cgroup,避免全节点高频追踪造成额外负载。
eBPF 提供证据,仍需与业务链路交叉验证。
数据解释也有边界,例如 kprobe 看到内核函数并不直接等于业务因果,需要与应用 trace 和时间线关联。短时 eBPF off-CPU 聚合发现线程阻塞在 DNS socket 接收,TCP 工具显示没有显著重传,最终定位到解析器串行等待。
如何正确使用 perf 和火焰图,避免得到误导结论?¶
技术说明
perf stat 适合比较周期、指令、缓存未命中和上下文切换等计数器;例如以 perf record -F 99 -g -- sleep 30 做有界调用栈采样,火焰图横向宽度代表样本占比,不代表时间顺序。要得到可解释栈,需要保留符号、构建 ID、调试信息,并针对帧指针省略、JIT 代码或容器路径配置 unwind 与符号解析。采样频率过高会扰动系统,过低则可能错过短热点。
on-CPU 火焰图显示正在消耗 CPU 的路径,不能解释锁、I/O 等等待;后者需 off-CPU、调度或语言级 profile。比较发布前后应保持相同流量、采样时长和 CPU 配额,最好用差分火焰图或按每请求 CPU 标准化。内核的 perf_event_paranoid 与容器 capability 限制有安全意义,不应为了方便永久开放给所有工作负载。
实践经验
复合场景中,新版 CPU 增加 30%,首张火焰图显示哈希函数变宽。按每请求归一后发现流量结构也改变;受控回放证实真正回归是日志字段格式化。团队关闭高基数字段止血,长期让基准覆盖真实载荷并保存构建符号。采样期将频率控制在可接受范围,验证 profiler 开启前后 P99 无显著偏移。
面试回答要点
火焰图宽度是样本占比,不是调用顺序或绝对耗时。
perf stat 适合比较周期、指令、缓存未命中和上下文切换等计数器;例如以 perf record -F 99 -g -- sleep 30 做有界调用栈采样,火焰图横向宽度代表样本占比,不代表时间顺序。复合场景中,新版 CPU 增加 30%,首张火焰图显示哈希函数变宽。
区分 on-CPU、off-CPU 和语言运行时视角。
on-CPU 火焰图显示正在消耗 CPU 的路径,不能解释锁、I/O 等等待;后者需 off-CPU、调度或语言级 profile。复合场景中,新版 CPU 增加 30%,首张火焰图显示哈希函数变宽。
保证符号、栈展开、流量与配额条件可比。
比较发布前后应保持相同流量、采样时长和 CPU 配额,最好用差分火焰图或按每请求 CPU 标准化。按每请求归一后发现流量结构也改变;受控回放证实真正回归是日志字段格式化。
用每请求成本和受控实验验证因果。
perf stat 适合比较周期、指令、缓存未命中和上下文切换等计数器;例如以 perf record -F 99 -g -- sleep 30 做有界调用栈采样,火焰图横向宽度代表样本占比,不代表时间顺序。按每请求归一后发现流量结构也改变;受控回放证实真正回归是日志字段格式化。
sysctl 性能调优应遵循什么方法,哪些“万能参数”最危险?¶
技术说明
sysctl 修改内核运行参数,作用域可能是全局或 network namespace,且不同内核版本语义与默认值会变化。调优应从可复现瓶颈出发:记录当前值和基线,理解参数控制的队列/内存/超时,单变量或小批量金丝雀修改,再看业务 SLO、错误和资源副作用。配置持久化后还要验证启动顺序及容器是否真正继承目标值。
危险做法包括无证据地把 socket buffer、backlog、文件句柄设到极大,任意缩短 TCP 超时或启用过时选项。大队列可能只把丢包变成排队,形成 bufferbloat;更大网络缓冲乘以连接数会吃掉内存;降低重试可能在短暂抖动时制造失败。调 vm.*、net.* 前应同时检查应用连接池、负载均衡器和 cgroup 上限。
实践经验
复合场景中,为解决连接丢弃,有人把 listen backlog 提高百倍。丢弃下降但请求 P99 增至数秒,因为过载请求只是在更长队列里等待。团队恢复原值、按入口令牌桶限流,并扩容处理能力;长期用到达率、服务时间和允许等待预算计算队列,sysctl 变更走版本化审查及回滚。副作用是过载时更早返回 429,但客户端能及时退避。
面试回答要点
参数调优必须有瓶颈模型、基线、金丝雀和回滚。
调优应从可复现瓶颈出发:记录当前值和基线,理解参数控制的队列/内存/超时,单变量或小批量金丝雀修改,再看业务 SLO、错误和资源副作用。团队恢复原值、按入口令牌桶限流,并扩容处理能力;长期用到达率、服务时间和允许等待预算计算队列,sysctl 变更走版本化审查及回滚。
大队列可能隐藏过载并恶化尾延迟。
大队列可能只把丢包变成排队,形成 bufferbloat;更大网络缓冲乘以连接数会吃掉内存;降低重试可能在短暂抖动时制造失败。团队恢复原值、按入口令牌桶限流,并扩容处理能力;长期用到达率、服务时间和允许等待预算计算队列,sysctl 变更走版本化审查及回滚。
计算每连接/每对象内存的乘数效应。
大队列可能只把丢包变成排队,形成 bufferbloat;更大网络缓冲乘以连接数会吃掉内存;降低重试可能在短暂抖动时制造失败。复合场景中,为解决连接丢弃,有人把 listen backlog 提高百倍。
核验内核版本、namespace 作用域和持久化结果。
sysctl 修改内核运行参数,作用域可能是全局或 network namespace,且不同内核版本语义与默认值会变化。团队恢复原值、按入口令牌桶限流,并扩容处理能力;长期用到达率、服务时间和允许等待预算计算队列,sysctl 变更走版本化审查及回滚。
日志轮转、journald 与磁盘保护应如何设计?¶
技术说明
日志链路要限制产生、缓冲、落盘和保留四个环节。传统文件可用 logrotate 按尺寸/时间 rename、压缩并通知应用 reopen;copytruncate 对不支持 reopen 的程序兼容,但存在复制窗口丢失和额外 I/O。journald 可按 SystemMaxUse、RuntimeMaxUse、MaxRetentionSec 等限制,journalctl --disk-usage 验证;日志转发器还需有磁盘缓冲上限、背压和丢弃策略。
“绝不丢日志”在磁盘有限时不可实现,必须按审计、安全、业务调试分级。关键审计日志远端同步并防篡改,普通 debug 可采样;高基数字段与异常堆栈应限速。磁盘需预留紧急水位,写满时优先保护业务数据盘,且监控字节、inode、deleted-open 文件及转发积压。轮转任务要避免同一时刻压缩造成 CPU/I/O 峰值。
实践经验
复合场景中,下游日志平台中断,代理无限落盘把根分区写满,节点服务异常。团队先暂停 debug、限制代理缓冲并转移非关键日志,保留审计流;长期独立日志分区、设置分级配额和水位降级,轮转时间加随机抖动。降级会丢弃低优先级日志,因此同时输出丢弃计数并在事故记录中标注可见性缺口。
面试回答要点
轮转优先 rename+reopen,理解 copytruncate 丢失窗口。
传统文件可用 logrotate 按尺寸/时间 rename、压缩并通知应用 reopen;copytruncate 对不支持 reopen 的程序兼容,但存在复制窗口丢失和额外 I/O。团队先暂停 debug、限制代理缓冲并转移非关键日志,保留审计流;长期独立日志分区、设置分级配额和水位降级,轮转时间加随机抖动。
产生速率、缓冲上限、保留期和远端背压一起控制。
journald 可按 SystemMaxUse、RuntimeMaxUse、MaxRetentionSec 等限制,journalctl --disk-usage 验证;日志转发器还需有磁盘缓冲上限、背压和丢弃策略。团队先暂停 debug、限制代理缓冲并转移非关键日志,保留审计流;长期独立日志分区、设置分级配额和水位降级,轮转时间加随机抖动。
日志分级,磁盘压力时保护业务与审计数据。
“绝不丢日志”在磁盘有限时不可实现,必须按审计、安全、业务调试分级。团队先暂停 debug、限制代理缓冲并转移非关键日志,保留审计流;长期独立日志分区、设置分级配额和水位降级,轮转时间加随机抖动。
同时监控容量、inode、积压和丢弃量。
磁盘需预留紧急水位,写满时优先保护业务数据盘,且监控字节、inode、deleted-open 文件及转发积压。降级会丢弃低优先级日志,因此同时输出丢弃计数并在事故记录中标注可见性缺口。
Linux 启动失败时如何在不扩大损坏的前提下恢复?¶
技术说明
启动路径大致为固件→引导器→内核/initramfs→根文件系统→PID 1/systemd→目标单元。先确定失败层级:控制台/串口是否看到内核,是否能找到根卷,是否进入 emergency mode,还是某关键 unit 超时。使用上一次可用内核、引导器临时参数、救援环境挂载根卷,结合 journalctl -b -1、dmesg、systemctl --failed 和依赖链收集证据。
恢复原则是先做快照或只读检查,再修改。文件系统工具必须针对未挂载或只读卷并使用对应类型;不能对已读写挂载的生产文件系统盲目 fsck。若由 /etc/fstab 非关键挂载阻塞,可临时修正 UUID 或使用合理的 nofail/超时,但数据库盘不能为追求启动成功而静默跳过。修复后要验证根因、数据一致性和完整重启,而非仅“能登录”。
实践经验
复合场景中,内核升级后部分节点停在 initramfs,控制台显示找不到加密根卷所需模块。团队冻结节点池升级,回选旧内核启动并摘流量;随后补齐 initramfs 模块、在同机型金丝雀做断电重启。回退旧内核会暂时保留安全漏洞,因此限定时间窗、隔离暴露面,并把可启动性与远程控制台纳入升级门禁。
面试回答要点
按启动层级定位,优先保全控制台与上一启动日志。
先确定失败层级:控制台/串口是否看到内核,是否能找到根卷,是否进入 emergency mode,还是某关键 unit 超时。回退旧内核会暂时保留安全漏洞,因此限定时间窗、隔离暴露面,并把可启动性与远程控制台纳入升级门禁。
先快照/只读检查,谨慎执行文件系统修复。
恢复原则是先做快照或只读检查,再修改。复合场景中,内核升级后部分节点停在 initramfs,控制台显示找不到加密根卷所需模块。
非关键与关键挂载采用不同失败策略。
若由 /etc/fstab 非关键挂载阻塞,可临时修正 UUID 或使用合理的 nofail/超时,但数据库盘不能为追求启动成功而静默跳过。团队冻结节点池升级,回选旧内核启动并摘流量;随后补齐 initramfs 模块、在同机型金丝雀做断电重启。
恢复后验证数据一致性、重启和升级回滚能力。
修复后要验证根因、数据一致性和完整重启,而非仅“能登录”。团队冻结节点池升级,回选旧内核启动并摘流量;随后补齐 initramfs 模块、在同机型金丝雀做断电重启。
-
USE 是 Utilization、Saturation、Errors(利用率、饱和度、错误)的缩写,是按资源逐项检查性能瓶颈的方法。 ↩
-
CPU 是 Central Processing Unit(中央处理器),负责执行程序指令;排障时需同时观察总利用率、单核热点和调度等待。 ↩
-
cgroup 是 Linux control group(控制组),用于限制、统计和隔离一组进程的 CPU、内存及 I/O 等资源。参见 Linux 内核 cgroup v2 文档。 ↩
-
I/O 是 Input/Output(输入/输出)的缩写,泛指程序与磁盘、网络等外部设备之间的数据读写。 ↩
-
RTT 是 Round-Trip Time(往返时延),表示数据从发送端到接收端并返回所需的时间。 ↩
-
API 是 Application Programming Interface(应用程序编程接口),定义软件组件之间如何发起请求和交换数据。 ↩
-
P99 是第 99 百分位数,表示 99% 的观测值不超过该值,常用来衡量少数慢请求造成的尾延迟。 ↩