Linux 性能分析与故障排查面试题
14 道题- 分类
- Linux
- 题目数
- 14 道
1 top / vmstat / iostat / perf / strace / bpftrace 等性能分析工具有什么区别?如何选用?
答案:
第一梯队:全局性能快照
top/htop:实时进程级 CPU、内存、负载信息;/proc/<pid>/stat数据聚合;快速定位可疑进程。vmstat 1:系统级 CPU(us/sy/wa/id)、内存(si/so)、IO(bi/bo)、系统调用(in/cs)、上下文切换(cs)每秒采样;r列持续 > CPU 核心数表示 CPU 饱和。iostat -dx 1:块设备层 IOPS、await、svctm、%util;%util≠ IO 饱和度(高 IO 队列时可能 100% 但仍有性能),更应看aqu-sz和rareq-sz。mpstat -P ALL 1:每核 CPU 分布,识别单核热点(常见于锁竞争)。sar -n DEV 1:网卡 PPS / 带宽,定位丢包(rxdrop/s、txdrop/s)。
第二梯队:进程级深挖
perf top/perf record -g:基于硬件性能计数器(PMU)的采样 profiler;定位 CPU 热点函数、内核热点。strace -p <pid>/strace -c -p <pid>:跟踪进程系统调用与信号;诊断进程卡在内核哪一步(如futex、epoll_wait)。ltrace:跟踪库函数调用。
第三梯队:高级内核分析
bpftrace(eBPF):动态追踪,跟踪任意内核/用户态函数 + 上下文;如bpftrace -e 'kprobe:do_sys_open { printf("%s\n", comm); }'。perf trace:类似 strace 但基于 eBPF,性能开销极低。ftrace:内核函数跟踪器,轻量。systemtap:类 DTrace 内核分析工具,脚本化。
选用决策树:
系统慢 → top/vmstat/mpstat/iostat/sar → 找到瓶颈维度
├─ CPU 热点 → perf top / bpftrace
├─ IO 等待 → iostat / blktrace / bpftrace biolatency
├─ 网络丢包 → sar -n / nicstat / tcpdump
└─ 进程卡顿 → strace / perf trace
2 cgroups 与 namespace 的作用分别是什么?容器是如何利用两者实现资源隔离的?
答案:
namespace(命名空间)—— 资源视图隔离:
- 提供进程视角的资源视图隔离,使进程组看不到其他进程组的资源。
- 8 种 namespace:mount、UTS、IPC、PID、network、user、cgroup(v4.6+)、time(v5.6+)。
- 系统调用:
clone(CLONE_NEWNS | CLONE_NEWPID | ...)或unshare()。 - PID namespace 实现 PID 重新编号(容器内 PID 1)、network namespace 提供独立网络栈(veth pair 连接)、mount namespace 提供独立文件系统视图。
cgroups(Control Groups,cgroup v2 推荐)—— 资源限制与统计:
- 提供 CPU、内存、IO、网络、进程数等资源的限额、统计、优先级控制。
- 层级结构:
/sys/fs/cgroup/<subsystem>/<cgroup-path>/。 - cgroup v2 统一接口、引入
memory.high(软限制触发回收)、memory.max(硬限制触发 OOM)、io.max(BFQ/io.weight)。 - 关键控制器:
cpu.max:quota period(如100000 100000即 1 核)。memory.max:硬限制。io.max:major:minor rbps=10485760 wbps=10485760。pids.max:进程数限制。
容器实现:
// 容器进程伪代码
unshare(CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWUTS);
mount("proc", "/proc", "proc", 0, NULL);
write_cgroup("memory.max", "536870912");
execvp(argv[0], argv);
OCI 运行时(runc / crun) 串联 namespace + cgroup + seccomp + capabilities + AppArmor/SELinux 形成完整容器边界。Docker、containerd、Kubernetes 容器均基于此实现。
3 SELinux / AppArmor / capabilities 三种安全机制有何区别?生产环境如何选用?
答案:
SELinux(Security-Enhanced Linux,NSA + Red Hat 主导):
- 基于**标签(label)**的强制访问控制(MAC):每个文件、进程、端口都有安全上下文(
user:role:type:sensitivity),策略定义 type 间的访问规则。 - 策略语言:M4 宏 + 模块化策略。
- 工作模式:
enforcing(强制执行)、permissive(仅告警)、disabled(关闭)。 - 优势:粒度极细,RHEL/CentOS 默认开启;劣势:策略复杂、配置学习成本高。
AppArmor(Canonical/Ubuntu 主导):
- 基于混合模型(路径 + capability + 网络能力),配置文件以
#include <tunables/global>开头,规则示例:/usr/sbin/nginx { file read, network inet stream, capability net_bind_service; }。 - 优势:易上手、路径化、与 Docker 默认集成(
docker-defaultprofile);劣势:粒度不如 SELinux。
capabilities(Linux capabilities):
- 将 root 权限拆分为 40+ 个细粒度能力(
CAP_NET_ADMIN、CAP_SYS_ADMIN、CAP_NET_BIND_SERVICE、CAP_DAC_OVERRIDE等)。 - 替代方案:传统 SUID root(粗糙),用 capabilities 让进程仅拥有必要权限。
- 容器默认丢弃大量 capabilities(Docker
--cap-drop=ALL --cap-add=NET_BIND_SERVICE)。
对比:
| 维度 | SELinux | AppArmor | capabilities |
|---|---|---|---|
| 模型 | 标签 (TE/RBAC) | 路径 | 能力位 |
| 复杂度 | 高 | 中 | 低 |
| 粒度 | 极细 | 中 | 粗(仅 root 操作类) |
| 集成 | RHEL 系 | Ubuntu / SUSE | 全平台 |
生产建议:
- RHEL / CentOS 保持 SELinux enforcing,重要业务进程开发自定义
.te模块。 - Ubuntu / Debian 使用 AppArmor profile,关键服务定制 profile。
- 容器:所有 capabilities 丢弃后按需添加(
--cap-drop=ALL --cap-add=NET_BIND_SERVICE),同时启用 seccomp 默认 profile。
4 dmesg / journalctl / core dump 各自的用途是什么?生产环境如何进行系统性故障排查?
答案:
dmesg(内核环形缓冲区)—— 内核事件:
- 记录内核启动日志、硬件探测、Oops/Panic、内核模块加载、磁盘错误、内存硬件错误(
mce)等。 dmesg -T显示人类可读时间;dmesg -l err,warn按级别过滤。- 生产关键场景:磁盘 IO 错误(
ata1: soft reset failed)、OOM Killer 记录(Out of memory: Killed process)、硬件故障(mce: [Hardware Error])。
journalctl(systemd 日志)—— 系统服务日志:
替代传统
syslog,统一收集内核、systemd、服务、审计日志,按服务、优先级、时间、进程 PID 过滤。持久化配置:
/etc/systemd/journald.conf中Storage=persistent。常用命令:
journalctl -u nginx --since "1 hour ago" journalctl -p err -b # 当前启动的错误日志 journalctl _PID=1234 # 按 PID 过滤 journalctl -k # 仅内核日志(等同 dmesg) journalctl --vacuum-time=7d # 清理 7 天前日志
**core dump(核心转储)—— 进程崩溃快照:**
- 进程崩溃(SIGSEGV、SIGABRT、SIGBUS)时将**内存、寄存器、栈**写入文件,便于事后分析。
- 配置 `coredumpctl`(systemd-coredump)集中管理。
```bash
echo '/tmp/core.%e.%p' > /proc/sys/kernel/core_pattern
ulimit -c unlimited
# 启用 systemd-coredump
systemctl enable systemd-coredump.socket
- 分析工具:
gdb <binary> <core-file>、coredumpctl gdb。
系统性故障排查流程:
1. 现象确认 → 监控/告警/用户反馈
2. 时间点定位 → 关联监控指标(Prometheus)、日志时间戳
3. dmesg 优先 → 硬件/内核问题(OOM、disk error、mce、kernel panic)
4. journalctl 服务日志 → 应用层异常(启动失败、配置错误)
5. 进程状态 → strace / gdb / /proc/<pid>/status
6. 资源使用 → vmstat / iostat / mpstat / perf / bpftrace
7. 网络层 → ss / netstat / tcpdump / nicstat
8. 根因定位 → 横向对比(正常 vs 异常)、纵向时间线(何时开始变化)
9. 修复 → 规避 → 复盘 → 文档
关键命令速查:
ss -s # 套接字统计
lsof -p <pid> # 进程打开文件
strace -p <pid> # 进程系统调用
perf top -g # 实时热点函数
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'
生产铁律:
- 所有节点开启
kernel.panic=10自动重启、kdump保留内核崩溃现场。 - journald 日志集中收集(ELK / Loki)+ 监控告警(
log_level=error即触发)。 - 关键进程开启 core dump,按需转储不滥用磁盘。
5 Linux 系统启动流程详解:BIOS→GRUB→内核→initramfs→systemd 各阶段发生了什么?
答案:
理解启动流程是排障"卡启动"问题的前提。
完整启动流程:
flowchart TD
S1["1. 硬件上电(Power On)
CPU 从 reset vector 跳到固件入口"]
S2["2. 固件阶段(BIOS/UEFI)
POST → 硬件自检
BIOS: MBR 模式加载 446 字节引导程序
UEFI: ESP 分区读取 EFI 应用(grubx64.efi)
加载 ACPI 表、SMBIOS 信息"]
S3["3. 引导加载器(GRUB2)
/boot/grub/grub.cfg → 内核 + initramfs 路径
显示菜单 → 加载 vmlinuz 到内存
加载 initramfs(cpio 归档,临时根文件系统)"]
S4["4. 内核自解压 + 启动
start_kernel() → setup_arch() → mm_init()
初始化调度器、内存管理、rootfs
挂载 initramfs 作为临时根
启动 init 进程(PID 1)→ 执行 /init"]
S5["5. initramfs 阶段(早期用户空间)
/init 脚本(dracut 生成的 systemd)
加载内核模块(lvm/dm/fs/storage 驱动)
组装 RAID / 解密 LUKS / 激活 LVM VG
挂载真实根到 /sysroot → switch_root"]
S6["6. systemd 阶段(PID 1)
加载 unit 配置(/etc/systemd、/usr/lib)
解析依赖图 → 并行启动 unit
激活 default.target(graphical/multi-user)
getty → login → 用户登录"]
S1 --> S2 --> S3 --> S4 --> S5 --> S6
每个阶段的排障方法:
阶段 2-3 失败(开机黑屏或 GRUB rescue):
# 现象:屏幕显示 "grub rescue>"
# 原因:GRUB 找不到正常启动项
# 进入 GRUB 命令行手动启动
grub rescue> set root=(hd0,msdos1) # 引导分区
grub rescue> linux /vmlinuz-<ver> root=/dev/sda2 ro
grub rescue> initrd /initramfs-<ver>.img
grub rescue> boot
阶段 4-5 失败(kernel panic 或 initramfs drop to shell):
# 现象:"You are now being dropped into an emergency shell"
# 原因:根文件系统找不到(驱动缺失、LVM 未激活、UUID 错)
# 在 emergency shell 中排查
ls /dev/sd* /dev/vd* /dev/nvme* # 看可用设备
blkid # 看文件系统 UUID
ls /lib/modules/$(uname -r) # 看已加载模块
cat /proc/modules # 确认驱动加载情况
dmesg | tail -30 # 内核日志
# 修复:手动挂载根 + 修复 fstab
mount /dev/sda2 /sysroot
mount -o bind /dev /sysroot/dev
chroot /sysroot
vi /etc/fstab
阶段 6 失败(系统卡在某个 service):
# 在 GRUB 启动参数加 systemd.unit=rescue.target
# 编辑 /etc/default/grub
GRUB_CMDLINE_LINUX="... systemd.unit=rescue.target"
grub2-mkconfig -o /boot/grub2/grub.cfg
# rescue.target 启动后
systemctl list-jobs # 看卡在哪个 job
systemctl status # 看失败 unit
journalctl -xb # 看本次启动日志
systemctl reset-failed # 重置失败状态
关键启动日志:
# 内核日志(启动过程)
dmesg | less
# 启动耗时分解
systemd-analyze
# Startup finished in 2.456s (kernel) + 8.123s (userspace) = 10.579s
# graphical.target reached after 9.876s in userspace
# 启动耗时排序
systemd-analyze blame | head -20
# 关键路径
systemd-analyze critical-chain multi-user.target
生产实践:
- K8s 节点:systemd 还要管理 kubelet/containerd,节点启动慢的常见原因是
kubelet.service等待containerd.service - 启动慢的常见原因:
NetworkManager-wait-online.service(等网络就绪,可禁用)- 大量 mount unit(
/etc/fstab配nofail跳过) - LVM 激活慢(
lvm.conf的use_lvmetad)
- initramfs 自定义:
dracut -f --add "lvm network" /boot/initramfs-$(uname -r).img
6 关键 sysctl 参数分类与推荐配置(生产级基线)
答案:
sysctl 调优是 SRE 日常必做。下面按子系统分类整理生产基线:
网络子系统(net.*):
# === 1. 缓冲区 ===
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# === 2. 拥塞控制 ===
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
# === 3. 连接队列 ===
net.core.somaxconn = 32768 # 监听 backlog 上限
net.ipv4.tcp_max_syn_backlog = 65535 # SYN 队列
net.core.netdev_max_backlog = 5000 # 网卡接收队列
# === 4. TIME_WAIT 优化 ===
net.ipv4.tcp_tw_reuse = 1 # 允许 TIME_WAIT 重用
net.ipv4.tcp_fin_timeout = 15 # 默认 60s → 15s
# === 5. SYN 防护 ===
net.ipv4.tcp_syncookies = 1 # 开启 SYN Cookie
net.ipv4.tcp_synack_retries = 2 # SYN+ACK 重试 2 次
net.ipv4.tcp_max_syn_backlog = 65535
# === 6. Keepalive ===
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
# === 7. 安全 ===
net.ipv4.tcp_rfc1337 = 1 # 防御 TIME_WAIT 攻击
net.ipv4.conf.all.rp_filter = 1 # 反向路径过滤
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
# === 8. IP 转发(如需 NAT/路由) ===
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
# === 9. TCP Fast Open ===
net.ipv4.tcp_fastopen = 3
# === 10. 端口范围 ===
net.ipv4.ip_local_port_range = 10000 65535
虚拟内存子系统(vm.*):
# === 1. 脏页回写 ===
vm.dirty_background_ratio = 10 # 内存 10% 脏页时后台回写
vm.dirty_ratio = 20 # 内存 20% 脏页时同步阻塞
vm.dirty_expire_centisecs = 3000 # 30s
vm.dirty_writeback_centisecs = 500 # 5s 唤醒一次回写
# === 2. Swap 倾向 ===
vm.swappiness = 60 # 默认 60
# 数据库:vm.swappiness = 1(优先用内存)
# === 3. 缓存压力 ===
vm.vfs_cache_pressure = 100 # 默认 100
# 文件服务器可调高(>100)优先回收 dentry
# === 4. 大页(数据库) ===
vm.nr_hugepages = 1024 # 2GB
# === 5. 透明大页(生产数据库关闭) ===
# echo never > /sys/kernel/mm/transparent_hugepage/enabled
# === 6. OOM 行为 ===
vm.overcommit_memory = 0 # 0=启发式 1=总允许 2=严格
vm.overcommit_ratio = 50
vm.oom_kill_allocating_task = 0 # 默认杀占用最高,可改 1 杀触发者
内核子系统(kernel.*):
# === 1. 进程数限制 ===
kernel.pid_max = 4194304
kernel.threads-max = 4194304
# === 2. 文件句柄 ===
fs.file-max = 2097152 # 系统级
# 进程级:ulimit -n / /etc/security/limits.conf
# === 3. 信号队列 ===
kernel.sem = 250 32000 32 128
# === 4. 共享内存 ===
kernel.shmmax = 68719476736 # 64GB
kernel.shmall = 4294967296
# === 5. 消息队列 ===
kernel.msgmax = 65536
kernel.msgmnb = 65536
# === 6. NUMA(数据库可关) ===
kernel.numa_balancing = 0 # 0=关闭 1=启用
# === 7. 软死锁检测 ===
# hung_task_timeout_secs 默认 120,调小可更快发现卡死
# echo 30 > /proc/sys/kernel/hung_task_timeout_secs
文件系统(fs.*):
# === 1. inotify 限制(影响 K8s/IDE/agent) ===
fs.inotify.max_user_watches = 524288 # 默认 8192,K8s 节点需调大
fs.inotify.max_user_instances = 512
fs.inotify.max_queued_events = 16384
# === 2. file-max 已在 kernel. 中
# === 3. 目录项缓存 ===
fs.dir-notify-enable = 1
生产场景配置包:
高并发 Web 服务器:
# /etc/sysctl.d/99-web.conf
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288
K8s 节点:
# /etc/sysctl.d/99-k8s.conf
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 8192
fs.file-max = 2097152
kernel.pid_max = 4194304
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
vm.swappiness = 1 # 容器节点推荐 1(kubeadm 默认),避免 OOM 时完全无 swap 缓冲
数据库服务器:
# /etc/sysctl.d/99-db.conf
vm.swappiness = 1
vm.dirty_background_ratio = 3
vm.dirty_ratio = 10
vm.nr_hugepages = 16384 # 32GB 大页
kernel.numa_balancing = 0
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
应用方式:
# 1. 直接生效
sysctl -w net.core.somaxconn=32768
# 2. 从文件加载
sysctl -p /etc/sysctl.d/99-k8s.conf
# 3. 持久化(写入 /etc/sysctl.d/)
echo "net.core.somaxconn = 32768" > /etc/sysctl.d/99-k8s.conf
sysctl --system
# 4. 验证
sysctl net.core.somaxconn
生产建议:
- 用
sysctl --system一次性加载 /etc/sysctl.d/ 所有文件 - 修改前先
sysctl -w试运行,确认无问题再写文件 - K8s 节点用 tuned profile(如
tuned-adm profile virtual-host) - 生产变更通过 Ansible/Salt 推送,避免手工操作
7 /proc 伪文件系统详解:哪些关键路径必须掌握?
答案:
/proc 是 Linux 内核提供的伪文件系统(procfs),挂载在 /proc,提供进程和内核信息的运行时接口。几乎所有性能工具都依赖 /proc。
核心路径分类:
1. 系统级(无 PID 后缀):
# === 内存信息 ===
/proc/meminfo # 全局内存统计
# MemTotal: 67108864 kB
# MemFree: 12345678 kB
# MemAvailable: 45678901 kB # 真正可用(MemFree + 可回收缓存)
# Buffers: 234567 kB
# Cached: 12345678 kB
# SwapTotal/Cached/Available
# === CPU 信息 ===
/proc/cpuinfo # CPU 型号、频率、cache、flags
/proc/loadavg # 负载(1/5/15 分钟)+ running/total 进程数
# 0.50 0.45 0.40 1/1234 12345
/proc/stat # 全局 CPU 统计(user/nice/system/idle/iowait/irq/softirq/steal)
# cpu 1234567 0 234567 89012345 12345 0 6789 0 0 0
# 计算 CPU 使用率:100 - (idle4 - idle0) / (total4 - total0) * 100
# === 启动信息 ===
/proc/uptime # 启动秒数 + idle 秒数
/proc/version # 内核版本 + GCC 版本
/proc/cmdline # 内核启动参数
/proc/filesystems # 支持的文件系统类型
# === 中断信息 ===
/proc/interrupts # 硬件中断统计
/proc/softirqs # 软中断统计
# 看哪个 CPU 软中断高 → 网络瓶颈或调度问题
# === IO 信息 ===
/proc/diskstats # 块设备 IO 统计(iostat 数据源)
/proc/partitions # 分区信息
# === 网络信息 ===
/proc/net/dev # 网卡统计(bytes/packets/errors/drops)
/proc/net/tcp # TCP 连接表(netstat 数据源)
/proc/net/tcp6 # IPv6 TCP
/proc/net/udp # UDP
/proc/net/arp # ARP 表
/proc/net/route # 路由表
/proc/net/sockstat # socket 统计
# === 模块与符号 ===
/proc/modules # 已加载内核模块
/proc/kallsyms # 内核符号表(需 root)
# === cgroup 挂载点 ===
/proc/cgroups # cgroup 控制器信息
/proc/self/cgroup # 当前进程 cgroup 归属
/proc/<pid>/cgroup # 指定进程 cgroup
# === 挂载点 ===
/proc/mounts # 当前挂载(mount 命令数据源)
/proc/self/mountinfo # 详细挂载信息
2. 进程级(/proc/
# === 基础信息 ===
/proc/<pid>/cmdline # 启动命令行(\0 分隔)
/proc/<pid>/comm # 进程名
/proc/<pid>/status # 易读进程状态(vs stat)
# Name: nginx
# State: S (sleeping)
# Pid: 1234
# PPid: 1
# Threads: 8
# VmRSS: 123456 kB
# VmSize: 234567 kB
/proc/<pid>/stat # 详细状态(52 字段,空格分隔)
/proc/<pid>/statm # 简化 stat(size/resident/shared/text/lib/data)
# === 内存信息 ===
/proc/<pid>/maps # 虚拟内存映射(VMA 段)
/proc/<pid>/smaps # 每段详细 RSS/Pss 统计
/proc/<pid>/numa_maps # NUMA 内存分布
/proc/<pid>/oom_score # 0-1000
/proc/<pid>/oom_score_adj # -1000 ~ +1000
/proc/<pid>/oom_adj # 旧版
# === IO 信息 ===
/proc/<pid>/io # 进程 IO 统计(rchar/wchar/syscr/syscw/...)
# rchar: 123456789 读字节
# wchar: 987654321 写字节
# syscr: 12345 读系统调用
# syscw: 6789 写系统调用
# read_bytes: 23456789 真实磁盘读
# write_bytes: 9876543 真实磁盘写
# === 文件描述符 ===
/proc/<pid>/fd/ # fd 符号链接(0,1,2 是 stdin/stdout/stderr)
/proc/<pid>/fdinfo/ # fd 详细信息
/proc/<pid>/limits # 资源限制
# Max open files: 65535
# === 调度信息 ===
/proc/<pid>/sched # 调度统计(runtime/waittime/...)
/proc/<pid>/schedstat # 调度器统计
/proc/<pid>/cpuset # CPU 亲和性
/proc/<pid>/task/<tid>/ # 线程级(与进程级类似)
# === 网络 ===
/proc/<pid>/net/ # 进程 namespace 网络视图
/proc/<pid>/ns/ # namespace 引用
实战命令:
# 1. 查进程命令行(绕过 ps 解析问题)
cat /proc/1234/cmdline | tr '\0' ' '
# 2. 查进程打开的文件(lsof 备选)
ls -la /proc/1234/fd/ | head
# 3. 查进程 IO 统计
cat /proc/1234/io
# watch -n 1 'cat /proc/1234/io' # 持续观测
# 4. 查进程实际内存
cat /proc/1234/status | grep -E "VmRSS|VmSize|Threads"
# 5. 查谁在使用某端口
ls -la /proc/*/fd/ 2>/dev/null | grep socket | head
# 6. 系统内存可用量
awk '/MemAvailable/ {print $2 " kB"}' /proc/meminfo
# 7. 系统负载 + 内存 + 进程数
echo "load: $(cat /proc/loadavg | awk '{print $1,$2,$3}')"
echo "mem: $(awk '/MemAvailable/{a=$2}/MemTotal/{t=$2}END{printf "%.1f%% used", (t-a)/t*100}' /proc/meminfo)"
# 8. 找 CPU 占用最高的进程(无 ps/top)
for pid in /proc/[0-9]*; do
pid_num=$(basename $pid)
cpu=$(awk '{print "cpu "$2+$3+$4+$5+$6+$7+$8}' /proc/$pid_num/stat 2>/dev/null)
echo $pid_num $cpu
done | sort -k2 -rn | head -10
生产建议:
- 容器内
top/ps受 namespace 限制时,/proc仍可见(同一 PID namespace) /proc/sys/是 sysctl 的底层接口,可直接 echo 调参/proc/kcore是 ELF 格式的内核映像(GB 大小),勿读- k8s 中
kubectl debug node/<node>即可看到节点 /proc
8 bpftrace 与 perf 实战:如何用 eBPF 工具定位疑难问题?
答案:
eBPF(extended Berkeley Packet Filter)是 Linux 内核革命性技术,无需修改内核或加载模块就能在内核/用户态运行沙箱程序,性能开销极低(1-5%)。
bpftrace 入门:
# 安装(Ubuntu)
apt install bpftrace
# 或 RHEL
yum install bpftrace
# 1. Hello World(追踪 open 系统调用)
bpftrace -e 'kprobe:do_sys_open { printf("%s [%d]\n", comm, pid); }'
# 2. 看谁在读 /etc/passwd
bpftrace -e 'tracepoint:syscalls:sys_enter_openat /str(args->filename) == "/etc/passwd"/ { printf("%s\n", comm); }'
# 3. 统计进程系统调用次数(top 10)
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
# Ctrl-C 后输出 @comm[nginx]: 12345
# @comm[mysqld]: 6789
# 4. 跟踪进程系统调用(strace 增强版)
bpftrace -e 'tracepoint:syscalls:sys_enter_read /pid == 1234/ { printf("read %d bytes\n", args->count); }'
# 5. 统计进程退出时间
bpftrace -e 'tracepoint:sched:sched_process_exit { printf("%s exit code %d duration %d\n", comm, args->code, args->timestamp_ns - $self->start); }'
# 6. 看 TCP 重传统计
bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[args->sport] = count(); }'
bpftrace 脚本(保存为 .bt 文件):
#!/usr/bin/env bpftrace
/* biolatency.bt - 块设备 IO 延迟分布直方图 */
#include <linux/blkdev.h>
BEGIN {
printf("Tracing block device I/O... Hit Ctrl-C to end.\n");
}
kprobe:blk_mq_start_request {
@start[tid] = nsecs;
}
kretprobe:blk_mq_end_request /@start[tid]/ {
@usecs = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}
END {
printf("\nBlock IO latency (us):\n");
print(@usecs);
}
# 跑脚本
bpftrace biolatency.bt
# 1 -> 2 : 5 |
# 2 -> 4 : 15 ||
# 4 -> 8 : 156 ||||||||
# ...
# 512 -> 1024 : 3 |
# 1024 -> 2048 : 1 |
# @usecs:
# [1] 5 |@@@@|
# [2] 15 |@@@@@@@@@@@|
# [16] 156 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
# ...
perf 实战(采样型 profiler):
# 安装
apt install linux-tools-common linux-tools-generic
# 1. 实时 CPU 热点(按函数)
perf top
# 5.0% [kernel] [k] __schedule
# 3.0% mysqld [.] buf_calc_page_new_checksum
# ...
# 2. 记录 30 秒 profile
perf record -F 99 -a -g -- sleep 30
perf report --sort=comm,symbol
# 3. 看调用栈
perf record -F 99 -p 1234 -g -- sleep 10
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
# 4. 跟踪系统调用(类似 strace)
perf trace -p 1234
perf trace -e open,read,write -- your-command
# 5. 性能计数器(PMC)查询
perf stat -e cache-misses,cache-references,L1-dcache-load-misses -p 1234
# 6. 内存分配热点
perf mem record -t load -p 1234 -- sleep 10
perf mem report
# 7. 调度延迟分析
perf sched record -- sleep 10
perf sched latency
# 8. 锁竞争分析
perf lock contention -a -- sleep 10
常见生产场景:
场景 1:CPU 100% 不知是哪个函数
# 1. 找进程
top -bn1 | head
# PID 1234 mysqld 100% CPU
# 2. 采样(不需要停服务)
perf top -p 1234
# 或
perf record -F 99 -p 1234 -g -- sleep 30
perf report
场景 2:服务响应慢,卡在 IO
# 跟踪读/写系统调用
bpftrace -e '
kprobe:__x64_sys_read /pid == 1234/ {
@start[tid] = nsecs;
@fd[tid] = args->fd;
}
kretprobe:__x64_sys_read /@start[tid]/ {
$lat_us = (nsecs - @start[tid]) / 1000;
@usecs = hist($lat_us);
if ($lat_us > 10000) { # > 10ms 记录
printf("slow read: fd=%d lat=%d us\n", @fd[tid], $lat_us);
}
delete(@start[tid]); delete(@fd[tid]);
}'
场景 3:网络丢包位置
# 看内核丢包位置
perf trace -e skb:kfree_skb -a
# 看到大量 skb:kfree_skb 来自 tcp_v4_rcv → TCP 校验和错
# 或用 bpftrace
bpftrace -e '
kprobe:__kfree_skb /args->reason == SKB_DROP_REASON_TCP_CSUM/ {
printf("TCP CSUM drop: skb=%p reason=%d\n", args->skb, args->reason);
}'
场景 4:磁盘延迟分布
# 跑预先写好的脚本
bpftrace /usr/share/bpftrace/tools/biolatency.bt
# 看 IO 延迟直方图
# 99% IO 在 100μs 内 → NVMe 健康
# 99% IO > 10ms → 慢盘或队列深
bpftrace 工具集(已预装):
ls /usr/share/bpftrace/tools/
# bashreadline.bt - bash 击键统计
# biolatency.bt - 块 IO 延迟
# biosnoop.bt - 块 IO 跟踪
# capable.bt - 能力检查
# cpudist.bt - 进程 on-CPU 延迟
# dcsnoop.bt - 目录项缓存
# execsnoop.bt - 进程 exec 跟踪
# gethostlatency.bt - getaddrinfo 延迟
# killsnoop.bt - 进程 kill 跟踪
# mdflush.bt - 元数据 fsync
# naptime.bt - 进程 off-CPU 时间
# oomkill.bt - OOM Kill 事件
# opensnoop.bt - 进程 open 跟踪
# pidpersec.bt - 进程创建速率
# runqlat.bt - CPU 运行队列延迟
# syscount.bt - 系统调用计数
# tcpaccept.bt - TCP accept 跟踪
# tcpconnect.bt - TCP connect 跟踪
# tcpretrans.bt - TCP 重传
# vfsstat.bt - VFS 统计
生产实践:
- bpftrace 单次跑 < 30 分钟,避免长期挂载影响性能
- bpftrace 写脚本前先用
bpftrace --info看探针列表 - perf 长期跑用
perf record -F 99 -a -g -- sleep 86400一天后停 - FlameGraph(
flamegraph.pl)是 perf 数据可视化的标准工具 - 生产部署:考虑 bpftrace 程序安全——bpftrace 默认带 1M 指令上限 + 内核 verifier 静态检查,不会无限循环。主要风险:探针数量过多导致 attach 开销、BPF map 未设上限导致内存泄漏、高 QPS 下 printf 输出开销
9 Linux PSI(Pressure Stall Information)是什么?如何用于性能监控?
答案:
PSI(Pressure Stall Information,压力失速信息)是 Linux 4.20+ 引入的内核特性,量化系统因资源争用而损失的时间。比传统"CPU 利用率"更精准,直接反映"被卡了多久"。
三类资源 PSI:
# CPU
cat /proc/pressure/cpu
# some avg10=0.00 avg60=0.05 avg300=0.10 total=123456
# full avg10=0.00 avg60=0.00 avg300=0.00 total=0
# 内存
cat /proc/pressure/memory
# some avg10=2.30 avg60=1.50 avg300=0.80 total=987654321
# full avg10=0.00 avg60=0.00 avg300=0.00 total=0
# IO
cat /proc/pressure/io
# some avg10=5.20 avg60=3.10 avg300=2.40 total=1234567890
# full avg10=0.00 avg60=0.00 avg300=0.00 total=0
关键字段:
some:至少一个任务因该资源被阻塞(抢占/失忆/IO 等待)full:所有任务都被阻塞(完全无法工作)avg10/60/300:最近 10/60/300 秒的阻塞时间占比(百分比)total:从启动以来累计阻塞时间(微秒)
为什么需要 PSI:
- 传统监控:CPU 利用率 = 100%,但你不知道"卡了多久"
- PSI:直接告诉你 “最近 10 秒内,因为内存阻塞损失了 2.3 秒时间”
- 这对 SLO 监控至关重要(“99% 请求延迟 < 100ms” → “99% 时间 CPU 没 stall”)
典型解读:
| PSI 指标 | 含义 | 阈值告警 |
|---|---|---|
cpu/some > 5% | 频繁有任务等 CPU | 检查负载、CPU 核数 |
cpu/full > 1% | 频繁 100% 所有任务都等 CPU | 严重:CPU 满载 |
memory/some > 10% | 内存频繁紧张 | 检查内存泄漏、cgroup 限制 |
memory/full > 1% | 频繁 OOM 边缘 | 严重:扩容或减负载 |
io/some > 10% | 频繁 IO 等待 | 检查慢盘、队列深 |
io/full > 1% | 频繁所有任务都等 IO | 严重:盘故障或饱和 |
cgroup v2 PSI(Pod 级别):
# Pod 的 PSI(K8s 1.20+)
cat /sys/fs/cgroup/kubepods/burstable/pod<uid>/cpu.pressure
cat /sys/fs/cgroup/kubepods/burstable/pod<uid>/memory.pressure
cat /sys/fs/cgroup/kubepods/burstable/pod<uid>/io.pressure
# 单个容器
cat /sys/fs/cgroup/kubepods.slice/.../memory.pressure
Prometheus 监控集成:
# node-exporter 默认暴露 PSI 指标
node_pressure_cpu_waiting_seconds_total
node_pressure_memory_stalled_seconds_total
node_pressure_io_stalled_seconds_total
node_pressure_memory_full_seconds_total
# 告警规则示例
- alert: HighCPIPressure
expr: |
rate(node_pressure_cpu_waiting_seconds_total[1m]) > 0.2
for: 5m
labels:
severity: warning
annotations:
summary: "CPU 压力高"
- alert: CriticalMemoryPressure
expr: |
rate(node_pressure_memory_full_seconds_total[1m]) > 0.01
for: 2m
labels:
severity: critical
annotations:
summary: "内存严重不足"
实战案例:
案例 1:CPU 利用率高但服务慢
# 现象:top 显示 CPU 100%,但应用延迟高
# 诊断
cat /proc/pressure/cpu
# some avg10=45.00 → 45% 时间有任务等 CPU(CPU 不足)
# full avg10=2.00 → 2% 时间所有任务都等 CPU(CPU 饱和)
# 结论:CPU 真的不够,不是别的问题
# vs 如果是
# some avg10=45.00
# full avg10=0.00 → 0% 时间全等(虽然利用率高但还有余量)
# 结论:CPU 接近满载但还没瓶颈,应该查别的原因(IO?锁?)
案例 2:内存泄漏早期发现
# 监控 memory/some 趋势
watch -n 5 'cat /proc/pressure/memory'
# memory/some 持续上升 → 内存压力越来越大 → 可能是泄漏
# K8s Pod 级
kubectl exec -it <pod> -- cat /sys/fs/cgroup/memory.pressure
案例 3:数据库 IO 抖动
# 数据库延迟高
# 1. 看 IO PSI
cat /proc/pressure/io
# some avg10=15.20 → 15% 时间有任务等 IO
# 2. 看具体设备
iostat -dx 1
# 3. 关联监控
# 若 PSI 高 + iostat 看到 svctm 高 → 盘慢
# 若 PSI 高 + iostat 看到高 await → 队列深
# 若 PSI 高 + aqu-sz 低 → NVMe 设备自身慢
生产建议:
- PSI 是比利用率更准的指标,优先基于 PSI 配 SLO 告警
some是常态指标,full是极端指标(应保持 0)- K8s 节点开启 PSI,配合 Vertical Pod Autoscaler(VPA)做资源动态调整
- 容器内(无特权)可读 cgroup PSI,但需
resources.monitoring.path = /sys/fs/cgroup/...
10 CPU 使用率过高(> 80%)该如何系统化排查?
答案:
CPU 使用率高本身不是问题,问题在于使用率高的部分是不是业务预期的。可能是:正常业务流量、突发任务、失控的循环、GC、加密/序列化、调度器开销、内核中断。排查目标是找出占用 CPU 的代码。
Step 1: 区分 us / sy / wa / si / st / id
# 总体分布
top -b -n 1 | head -20
# %Cpu(s): 45.0 us, 30.0 sy, 0.0 ni, 20.0 id, 5.0 wa, 0.0 hi, 0.0 si, 0.0 st
# ^ ^ ^ ^ ^ ^
# | | | | | 虚拟化偷取(宿主机过载)
# | | | | 软中断(网络包处理)
# | | | 硬中断(网卡/磁盘)
# | | idle
# | 内核态
# 用户态
# vmstat 持续观察
vmstat 1 5
# 看 sy 高 = 系统调用/内核开销大
# 看 us 高 = 应用层热点
# 看 wa 高 = IO 等待
# 看 hi/si 高 = 软硬中断(网络包/IO 频繁)
Step 2: 定位到进程
# 进程级 CPU 占用
ps -eo pid,ppid,user,pcpu,pmem,etime,comm --sort=-pcpu | head -20
# 看是哪个用户的进程(区分业务/系统)
ps -eo user,pcpu --sort=-pcpu | awk 'NR>1 {sum[$1]+=$2} END {for (u in sum) print u, sum[u]}'
# 看是哪个线程
top -H -p <pid>
# 按 CPU 排序,记录最高 tid
Step 3: 定位到代码(on-CPU 火焰图)
# perf 采样
perf record -F 99 -p <pid> -g -- sleep 30
perf report --sort=comm,symbol # 看热点函数
perf script | stackcollapse-perf.pl | flamegraph.pl > cpu.svg
# 简化版:用 perf top 实时看
perf top -p <pid>
# 看到 hot 函数名后去代码查
Step 4: 常见热点与处理
| 现象 | 根因 | 处理 |
|---|---|---|
| 应用层单函数占比 > 30% | 算法热点 | 算法优化、加缓存、并行化 |
| GC 占用 > 20% | JVM 频繁 GC | 调堆大小、改 G1/ZGC |
| 系统调用占比高 | 频繁 IO / fork | 用 strace -c -p <pid> 看具体 syscall |
| 软中断占比高(si) | 网络包处理 | 多队列 RSS、irqbalance、DPDK |
| 内核态占比高(sy) | 锁竞争 / 调度 | 看 perf lock、减少锁粒度 |
| 大量 R 态任务在等 CPU | 线程数过多 | 减少线程、用协程 |
| 单核 100% 其他核空 | 单线程应用 | 绑核或多线程改造 |
| wa% 高 | IO 阻塞 | 见"磁盘 IO 性能"专项 |
Step 5: Java 进程 CPU 高
# 找到 CPU 高的线程
top -H -p <pid>
# tid 转 16 进制
printf '%x\n' <tid>
# jstack 找对应线程栈
jstack <pid> | grep -A 30 "nid=0x<hex>"
# 用 async-profiler 取火焰图
./profiler.sh -d 30 -e cpu -f /tmp/cpu.html <pid>
Step 6: 持续监控 + 告警
# 业务进程 CPU 突增
rate(container_cpu_usage_seconds_total{pod=~"myapp-.*"}[5m]) > 0.9
# 整体节点 CPU 接近饱和
1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) > 0.8
典型场景:
us=80% + sy=20%→ 应用热点,火焰图us=20% + si=60%→ 网络包处理,看网卡多队列wa=80%→ 实际不是 CPU 瓶颈,是 IO 瓶颈(CPU 等盘)容器内 CPU 监控 100% 但宿主机 CPU 低 → CPU limit 设小导致 throttling
# 看 throttling cat /sys/fs/cgroup/cpu.stat # nr_throttled 100 throttled_usec 12345678 # ^^^^^^^^^^ 有 100 次被限流处理:调大
cpu.cfs_quota_us/cpu.max,或改--cpu-manager=static+ Guaranteed pod QoS
11 服务器响应缓慢该如何系统化排查?
答案:
“服务器响应慢"是最高频的线上问题,根因分布很广:CPU、内存、磁盘、网络、应用、数据库、外部依赖。关键是快速定位瓶颈层(USE 方法:Utilization / Saturation / Errors)。
黄金排查流程(自顶向下):
[应用层] 应用响应慢
↓ 排查
[运行时] GC / 锁 / 线程池
↓ 排查
[OS 资源] CPU / 内存 / IO / 网络
↓ 排查
[依赖] 数据库 / 缓存 / 第三方 API
↓ 排查
[外部] 网络 / DNS / 运营商
Step 1: 看应用指标(先确定慢在哪)
# 业务侧:RT 是不是真涨了
# 看 APM / 监控 / 业务日志
# 应用层拆解 RT
curl -w "DNS: %{time_namelookup}\nConnect: %{time_connect}\nTLS: %{time_appconnect}\nTTFB: %{time_starttransfer}\nTotal: %{time_total}\n" \
-o /dev/null -s <url>
# DNS 慢 → 解析问题
# Connect 慢 → 网络/防火墙
# TLS 慢 → 证书/算法
# TTFB 慢 → 服务端处理
# Total-TTFB 慢 → 响应体下载
# 应用服务状态
systemctl status myapp
journalctl -u myapp -n 100 --no-pager
Step 2: 看运行时(JVM 视角)
# 线程数(线程池满 = 排队)
jstack <pid> | grep "java.lang.Thread.State" | sort | uniq -c | sort -rn
# BLOCKED 多 → 锁竞争
# WAITING 多 → 等待资源(数据库连接池、IO)
# RUNNABLE 多 → 真的在跑
# GC 频率
jstat -gcutil <pid> 1s 5
# YGC 列:每秒几次?老年代 Full GC 时间?
# 数据库连接池
# 看 active / idle / waiting
Step 3: 看系统资源(USE 方法)
| 资源 | Utilization | Saturation | Errors |
|---|---|---|---|
| CPU | top %Cpu | load average、PSI cpu | — |
| 内存 | free used | PSI memory、swap si/so | OOM Kill |
| 磁盘 | iostat %util | aqu-sz、await、PSI io | dmesg IO error |
| 网络 | nicstat 吞吐 | drops、collisions | ethtool -S errors |
# 30 秒快速诊断脚本
{
echo "=== uptime ==="; uptime
echo "=== top ==="; top -b -n 1 | head -20
echo "=== free ==="; free -h
echo "=== iostat ==="; iostat -dx 1 3
echo "=== vmstat ==="; vmstat 1 3
echo "=== ss ==="; ss -s
echo "=== D 态进程 ==="; ps -eo pid,ppid,stat,comm | awk '$3 ~ /D/'
echo "=== dmesg 错误 ==="; dmesg | tail -20
echo "=== PSI ==="; for f in cpu memory io; do echo "--- $f ---"; cat /proc/pressure/$f; done
} | tee /tmp/perf-$(date +%s).log
Step 4: 看依赖
# 数据库慢
# 看数据库监控:QPS、慢查询、连接数、InnoDB 行锁、buffer pool 命中率
# 看慢查询日志
mysql -e "SHOW FULL PROCESSLIST" | head
# 看 lock 等待
mysql -e "SELECT * FROM information_schema.innodb_trx"
# 看主从延迟
mysql -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master
# 缓存
redis-cli --latency # 持续测延迟
redis-cli INFO stats | grep instantaneous
# 外部 API:业务代码看对方响应时间,或抓包
Step 5: 性能基线 + 历史回放
# 用 atop 持续记录,每 10 分钟采样(默认 10min 历史)
atop -a -w /var/log/atop.log 60
# 回放历史(看问题发生时间点的状态)
atop -r /var/log/atop.log -b 14:30 -e 14:35
Step 6: 网络层慢
# TCP 重传率高 = 网络丢包
ss -s # 看 retrans
netstat -s | grep -i retrans
# 抓包看是否 RST / 重传
tcpdump -i eth0 -nn -w /tmp/cap.pcap 'port 80'
# Wireshark: Statistics → Conversations → 看每个流的 RTT
Step 7: 应急处置
# 1. 临时扩容(云上)
# 加 pod 副本 / 加机器
# 2. 限流
nginx: limit_req / limit_conn
应用: Sentinel / Envoy
# 3. 降级
关非核心功能,返回缓存
# 4. 重启
# 5. 回滚
典型场景:
- 凌晨 3 点批量任务致响应慢 → 看 cron、批处理 schedule
- 流量突增致响应慢 → 看 pv/uv 是否异常、是否被刷
- 数据库锁等待致响应慢 → 查 innodb_trx、pg_locks
- K8s pod 调度慢致响应慢 → 节点资源满、镜像拉取慢
- 看似随机慢 → 看
PSI cpu some是否有 spike,对应时间段 CPU 突发
12 服务器频繁重启可能有哪些原因?该如何排查?
答案:
服务器重启可能由硬件、内核、关键进程、资源耗尽、外部控制等多类原因触发,关键是保留现场:启动日志、kdump 核心转储、/var/log/messages、硬件指示灯。SRE 必问。
常见原因分类:
| 类别 | 典型原因 | 排查点 |
|---|---|---|
| 硬件 | 电源故障、CPU 过热、内存 ECC、硬盘 SMART | BMC/IPMI 日志、SMART、mcelog |
| 内核 | kernel panic、soft lockup、hard lockup、Oops | kdump、dmesg、/var/crash/ |
| 资源 | OOM、cgroup 限制、磁盘写满 | dmesg OOM、/var/log/syslog |
| 系统 | systemd 重启、rsyslog 配置、看门狗 | journalctl、/var/log/wtmp |
| 外部控制 | IPMI 远程断电、云厂商运维、负载均衡健康检查失败 | 云监控、控制台、API 审计 |
| 误操作 | 人为 reboot、脚本 bug | 命令历史、sudo 审计 |
Step 1: 看是 graceful 重启还是 crash
# 看重启时间 + 时长
last reboot
# reboot system boot 2.6.32 Mon Jun 8 09:00 - 14:00 (05:00)
# ^^^^^ 启动时间
# 频繁时间点 = 找出规律
# 看 wtmp 完整登录历史
last -f /var/log/wtmp
# 看 runlevel 切换
cat /var/log/wtmp | last -x
Step 2: 系统日志
# 找出最后一次启动之前的日志(关键)
journalctl --list-boots
# 0 = 当前
# -1 = 上一次
journalctl -b -1 -n 200 # 上一次启动的最后 200 行
journalctl -b -1 -p err # 只看错误
journalctl -k -b -1 # 只看内核
# 看重启前是否有 OOM
journalctl -k | grep -iE "oom|killed process|out of memory"
dmesg | grep -iE "oom|panic|lockup|bug|trace"
# systemd 主动重启?
journalctl -u myapp.service --since "2 hours ago"
# 看 Restart= 触发记录
Step 3: kernel panic / kdump
# 启用 kdump(开机预留内存给 kdump kernel)
systemctl enable kdump
systemctl start kdump
# 配置文件 /etc/kdump.conf
# crashkernel=256M 或 auto
# panic 后会自动生成 vmcore
ls -la /var/crash/
# drwxr-xr-x 2 root root 2024-06-08 14:00 127.0.0.1-2024-06-08-14:00:21/
# vmcore ← 核心转储
# vmcore-dmesg.txt
# 分析 vmcore
crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/.../vmcore
# crash> bt -a # 所有 CPU 调用栈
# crash> ps # 进程列表
# crash> kmem -i # 内存信息
# 或用 makedumpfile 提取关键信息(不需完整 crash)
makedumpfile --dump-dmesg /var/crash/.../vmcore /tmp/dmesg.txt
Step 4: 硬件层
# CPU 温度
sensors # lm-sensors
# coretemp-isa-0000
# Core 0: +45.0°C (high = +80.0°C, crit = +100.0°C)
# 硬盘 SMART
smartctl -a /dev/sda
# 看 Reallocated_Sector_Ct(重映射)、Current_Pending_Sector(待重映射)
# 内存 ECC 错误
mcelog # 老式
# 现代在 dmesg / RAS daemon
journalctl -k | grep -iE "mce|machine check"
# IPMI/BMC
ipmitool sel list # 系统事件日志
ipmitool sdr list # 传感器读数
# 电源稳定
ipmitool sensor get "PSU1 Status"
Step 5: OOM 与 cgroup
# OOM 记录
journalctl -k | grep -i oom
dmesg | grep -i oom
# "Out of memory: Killed process 1234 (myapp) ..."
# "total-vm:..., anon-rss:..., file-rss:..., shmem-rss:..."
# cgroup 内存限制触发
journalctl -k | grep -i "memory cgroup"
# "memory cgroup out of memory: Killed process 1234 (myapp) ..."
# "memory cgroup: charge failed"
# cgroup 触发的 OOM Killer
cat /sys/fs/cgroup/memory/events
# low 0
# high 0
# max 5 ← 触发 max 5 次
# oom 3 ← OOM Kill 3 次
# oom_kill 3
Step 6: 监控与告警配置
# 节点重启告警
node_boot_time_seconds # 当前时间 - 启动时间 < 600s = 刚启动
# 触发: time() - node_boot_time_seconds < 600
# 内核 panic
node_vmstat_panic # 如果能拿到
# OOM 事件
rate(node_vmstat_oom_kill[5m]) > 0
典型场景:
- 凌晨固定时间重启 → cron 任务、备份脚本、UPS 自检
- 每次重启都是 kernel panic → 驱动/硬件故障,启用 kdump 留现场
- OOM 反复触发 → 应用内存泄漏、cgroup limit 太小
- 偶发重启 + 无日志 → IPMI 远程断电(云厂商运维)、机房空调故障
- K8s 节点频繁 NotReady → 节点组件崩溃、kubelet 健康检查失败
13 服务器响应慢且 CPU 不高,可能是什么根因?
答案:
“CPU 不高但响应慢” 通常意味着等待(IO、锁、网络、依赖),而非计算。常见根因:磁盘 IO 慢、数据库慢、外部 API 慢、锁竞争、线程池满、配置错误(DNS 解析慢、文件描述符耗尽)。
核心排查思路:识别"在哪一层等"
应用层 wait
↓
[线程状态] BLOCKED / WAITING / TIMED_WAITING 多 = 等锁 / 等连接 / 等 IO
↓
[系统调用] 阻塞 IO read/write / connect / accept
↓
[内核等待] D 态进程 / iowait / netstat 大量 TIME_WAIT / SYN_SENT
↓
[外部依赖] 数据库 RT 高 / Redis 慢 / 第三方 API 超时
Step 1: 看线程都在干什么(Java 示例)
jstack <pid> > /tmp/jstack.log
# 状态统计
grep "java.lang.Thread.State" /tmp/jstack.log | sort | uniq -c | sort -rn
# 234 BLOCKED ← 锁竞争
# 1234 WAITING ← 等资源(连接池 / 队列)
# 567 TIMED_WAITING ← 等超时(sleep / IO 超时)
# 89 RUNNABLE ← 真正在跑
# 找 BLOCKED 线程
grep -A 5 "BLOCKED" /tmp/jstack.log | head -30
# 通常显示:waiting to lock <0x000000076bf12345>
# 找持有这把锁的线程
grep "locked <0x000000076bf12345>" /tmp/jstack.log
# 找 WAITING 的具体等待目标
grep -B 1 -A 10 "WAITING" /tmp/jstack.log | grep "at "
# 常见:at java.lang.Object.wait、at java.util.concurrent.locks.AbstractQueuedSynchronizer
# 进一步看是哪个类的 wait
Step 2: 系统调用层
# 看进程在哪些系统调用上阻塞
strace -p <pid> -c -f
# Ctrl+C 后看汇总
# 看 read/write/connect/accept 各多少
# 看具体调用栈
strace -p <pid> -k
# TID 1234: futex(0x7f1234, FUTEX_WAIT, ...
# __lll_lock_wait+0x16 (libpthread)
# __GI___pthread_mutex_lock+0x6e (libpthread)
# Java_java_util_concurrent_locks_ReentrantLock_lock+0x1a (libjvm)
# → 在等 Java ReentrantLock
# 网络 IO 阻塞
strace -e trace=read,write,recv,send,connect -p <pid>
# 看是不是 read() 一直不出
Step 3: IO 与依赖
# 看磁盘 IO
iostat -dx 1 5
# 看 await 高不高,aqu-sz 大不大
# 看具体进程 IO
iotop -oP
# 看网络连接状态
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
# 看 ESTABLISHED、SYN_SENT、CLOSE_WAIT 比例
# 大量 SYN_SENT → 目标端口不通
# 大量 CLOSE_WAIT → 本端没关连接
# 大量 TIME_WAIT → 高频短连接
# 数据库慢
mysql -e "SHOW ENGINE INNODB STATUS\G" | head -100
# 看 LATEST DETECTED DEADLOCK、最近事务
# 抓包看 RTT
tcpdump -i eth0 -nn -w /tmp/cap.pcap host <db_ip> and port 3306
# Wireshark: Statistics → Conversations → TCP → RTT
Step 4: 常见"等"对照
| 现象 | 根因 | 处理 |
|---|---|---|
| 大量 BLOCKED | 锁竞争 | 减少锁粒度、改读写锁、用无锁数据结构 |
大量 WAITING on LockSupport.parkNanos | 线程池排队满 | 加大线程池、用队列限流 |
大量 TIMED_WAITING on Object.wait(timeout) | 数据库/Redis 客户端超时 | 调超时、调连接池、查服务端 RT |
| 大量 D 态 | 磁盘 IO hung | 慢盘、NFS 卡死、设备 hung |
| 大量 SYN_SENT | 上游连不上 | 防火墙、网络、目标端口未开 |
| 大量 CLOSE_WAIT | 应用没 close | 找泄漏的代码路径 |
| iowait 高 | 磁盘慢 | 见"磁盘 IO"专项 |
| TCP 重传多 | 链路丢包 | mtr 找丢包点、联系运营商 |
Step 5: 配置错误类(容易被忽略)
# DNS 解析慢(每次请求解析 1-2s)
# 看 glibc nscd / systemd-resolved 缓存
strace -e trace=connect curl <url> 2>&1 | head
# 看有没有 connect 之前先调 getaddrinfo
# Nagle 算法 + 延迟 ACK 冲突(小包场景 RT 飙 40ms)
sysctl net.ipv4.tcp_nodelay=1 # 业务代码 setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
# 文件描述符耗尽(应用日志报 Too many open files)
ulimit -n
# 调大 /etc/security/limits.conf
# TCP backlog 满(accept queue 满,新连接丢弃)
ss -ltn
# Recv-Q 接近 Send-Q → 满了
# 应用层调 listen(backlog) 加大 + sysctl net.core.somaxconn
典型场景:
- “应用 RT 涨 10 倍,CPU 没涨” → 数据库慢查、连接池满、第三方 API 慢
- “JVM CPU 20% 但 RT 慢” → GC 频繁但采样窗口短,看
jstat -gcutil - “响应慢但监控都正常” → DNS 解析慢(每次请求多 1s)、TCP_NODELAY 未开
- “白天慢、晚上不慢” → 定时任务抢资源、数据库白天流量高
14 系统级核心性能调优:CPU/内存/IO/网络四大基线是什么?
答案:
生产级 Linux 服务器有一组通用基线调优(sysctl / ulimit / 文件系统 / cgroup),覆盖 CPU/内存/IO/网络四个维度。这些是 SRE 装机/调优的"标准动作",面试必问、运维必备。
CPU 基线:
# 调度器
sysctl -w kernel.sched_migration_cost_ns=5000000 # 减少跨核迁移
sysctl -w kernel.sched_min_granularity_ns=1000000 # 1ms 最小粒度
# 透明大页(数据库建议关闭 THP)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 频率调节
cpupower frequency-set -g performance # 服务器关节能
# 验证:cpupower frequency-info
内存基线:
# swappiness(数据库/延迟敏感 → 1-10)
sysctl -w vm.swappiness=10
# 脏页比例
sysctl -w vm.dirty_ratio=10 # 物理内存 10%
sysctl -w vm.dirty_background_ratio=5 # 5% 后开始后台回写
sysctl -w vm.dirty_expire_centisecs=3000 # 30s 过期
sysctl -w vm.dirty_writeback_centisecs=500 # 5s 触发回写
# 内核缓存压力
sysctl -w vm.vfs_cache_pressure=100 # 默认 100,200 = 更激进回收 dentry/inode
# 透明大页(如未关)
# 见 CPU 部分
# OOM 行为
sysctl -w vm.panic_on_oom=0 # 0=触发 OOM Killer,1=panic
sysctl -w vm.overcommit_memory=1 # 0=严格,1=总允许,2=比例
磁盘 IO 基线:
# IO 调度器(按设备选)
# NVMe:none(设备自身够快)
# SATA SSD / 虚拟化透传:none 或 kyber
# HDD / SATA SSD 普通:mq-deadline
echo mq-deadline > /sys/block/sda/queue/scheduler
# 队列深度
echo 1024 > /sys/block/sda/queue/nr_requests
echo 256 > /sys/block/nvme0n1/queue/nr_requests
# 预读(顺序读多则加大)
blockdev --setra 4096 /dev/sda # 4MB 预读
# 验证:blockdev --getra /dev/sda
# 文件系统 mount 选项
# /etc/fstab
# UUID=... /data xfs defaults,noatime,nodiratime,allocsize=64m 0 0
# noatime:禁用访问时间更新(IO 减少 30%+)
# xfs / ext4 都推荐
网络基线:
# 通用
sysctl -w net.core.somaxconn=32768 # accept 队列
sysctl -w net.core.netdev_max_backlog=16384 # 网卡 backlog
sysctl -w net.core.rmem_max=16777216 # 16MB 接收缓冲
sysctl -w net.core.wmem_max=16777216 # 16MB 发送缓冲
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
# TIME_WAIT
sysctl -w net.ipv4.tcp_tw_reuse=1 # 出向连接复用(安全)
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
sysctl -w net.ipv4.tcp_fin_timeout=15 # 缩短
# 连接跟踪(用 iptables/nft 时调大)
sysctl -w net.netfilter.nf_conntrack_max=1048576
# TCP 优化
sysctl -w net.ipv4.tcp_fastopen=3
sysctl -w net.ipv4.tcp_slow_start_after_idle=0 # 长连接带宽探测
sysctl -w net.ipv4.tcp_mtu_probing=1 # PMTUD 黑洞探测
sysctl -w net.core.default_qdisc=fq # BBR 友好
sysctl -w net.ipv4.tcp_congestion_control=bbr # BBR 拥塞控制
# 文件描述符
echo "* soft nofile 1048576" >> /etc/security/limits.conf
echo "* hard nofile 1048576" >> /etc/security/limits.conf
ulimit -n 1048576
容器化基线:
# kubelet 推荐
--cgroup-driver=systemd
--cpu-manager-policy=static
--topology-manager-policy=single-numa-node
--max-pods=110
# K8s 1.22+ 启用 swap(可选)
--feature-gates=NodeSwap=true
--fail-swap-on=false
# cgroup v2(K8s 1.25+ 默认)
# 配合 systemd cgroup_driver
应用侧建议基线:
| 应用 | 关键调优 |
|---|---|
| Nginx | worker_processes auto、worker_rlimit_nofile 1048576、multi_accept on、use epoll |
| MySQL | innodb_buffer_pool_size = 70% RAM、innodb_log_file_size = 4G、innodb_io_capacity = 1000+(NVMe) |
| Redis | maxmemory-policy allkeys-lru、io-threads 4、io-threads-do-reads yes |
| Kafka | num.network.threads = 8、num.io.threads = disk数 × 2、log.segment.bytes = 1G |
| Java | -Xms = -Xmx、UseG1GC、MaxRAMPercentage=75.0、+UseContainerSupport |
典型场景:
- 新机器装机 → 按上面基线跑一遍 sysctl / 调优
- 性能问题定位 → 对照基线找差异(哪个没设 / 哪个和业务不匹配)
- 容器化迁移 → 注意 ulimit / cgroup driver / 大页 等与宿主机的对齐
- 业务变更后 RT 涨 → 重新评估基线(流量特征变了,原来设的拥塞控制可能不优)