前言
Linux 作为服务器操作系统之王,承载着互联网基础设施中超过 90% 的工作负载。然而,默认内核参数是为通用场景设计的,面对高并发、低延迟或吞吐密集型业务时,未经调优的系统往往只能发挥出硬件潜力的 60%-70%。本文将从 CPU 调度器、内存管理、I/O 子系统、cgroup v2 资源隔离四个维度,深入剖析 Linux 性能调优的核心机制,并提供生产环境下的实战配置方案。
一、CPU 调度器深度剖析
1.1 CFS 完全公平调度器原理
Linux 默认使用 CFS(Completely Fair Scheduler)调度普通进程。CFS 的核心思想是维护每个进程的虚拟运行时间(vruntime),通过红黑树选择 vruntime 最小的进程执行。关键参数包括:
| 参数 | 默认值 | 说明 |
|---|---|---|
| sched_min_granularity_ns | 1000000 (1ms) | 最小调度粒度 |
| sched_wakeup_granularity_ns | 1500000 (1.5ms) | 唤醒抢占粒度 |
| sched_migration_cost_ns | 500000 (0.5ms) | 进程迁移代价估计 |
| sched_nr_migrate | 32 | 负载均衡时最大迁移进程数 |
性能场景调优建议:
- 计算密集型(HPC/编译):增大 sched_min_granularity_ns 至 10ms,减少上下文切换开销,让进程跑更长时间片
- 低延迟服务(交易/实时通信):减小至 0.5ms,加快响应时间;同时设置 sched_wakeup_granularity_ns 为 0.4ms 允许抢占
- Web 服务(混合负载):保持默认或微调至 2ms/2.5ms,平衡吞吐与延迟
1.2 实时调度策略与优先级
SCHED_FIFO 和 SCHED_RR 提供实时调度能力,优先级范围 1-99(数字越大越高)。生产中的高频交易系统可使用 SCHED_FIFO + 优先级 80-90 绑定专用核:
// 使用 chrt 设置实时优先级
sudo chrt -f 85 /opt/trading/engine
// 确认策略
chrt -p 1234
# 输出: pid 1234 的当前调度策略: SCHED_FIFO
# pid 1234 的当前优先级: 85
// taskset 绑定 CPU 核心
sudo taskset -pc 2,3 $(pidof engine)
1.3 CPU 亲缘性与 NUMA 优化
NUMA 架构下跨节点访问内存延迟可达本地节点的 2-3 倍。通过 numactl 可查看拓扑并绑定进程:
# 查看 NUMA 拓扑
numactl --hardware
# available: 2 nodes (0-1)
# node 0 cpus: 0 2 4 6 8 10 12 14
# node 1 cpus: 1 3 5 7 9 11 13 15
# node 0 size: 65408 MB
# node 1 size: 65536 MB
# 绑定进程到 NUMA node 0
numactl --cpunodebind=0 --membind=0 ./redis-server
# 或使用 libnuma 编程级绑定
numa_set_localalloc();
IRQ 中断亲和性绑定:将网卡中断绑定到特定核心,避免跨 NUMA 节点中断处理导致缓存失效。
# 查看网卡中断号
grep eth0 /proc/interrupts
# 假设中断号为 89
# 绑定中断到 CPU 0(使用位掩码)
echo 1 > /proc/irq/89/smp_affinity
# multi-queue 网卡多队列绑定
echo 0f > /proc/irq/90/smp_affinity # CPU 0-3
echo f0 > /proc/irq/91/smp_affinity # CPU 4-7
二、内存管理与 TLB/大页优化
2.1 HugePage 实战配置
标准 4KB 页面对大内存应用意味着庞大的页表开销。2MB/1GB 大页可减少 TLB Miss 率:
# 查看当前大页使用
grep Huge /proc/meminfo
# HugePages_Total: 512
# HugePages_Free: 512
# Hugepagesize: 2048 kB
# 运行时临时分配 512 个 2MB 大页
echo 512 > /proc/sys/vm/nr_hugepages
# 永久配置(/etc/sysctl.conf)
echo "vm.nr_hugepages = 512" >> /etc/sysctl.conf
sysctl -p
# /etc/fstab 挂载 hugetlbfs
mkdir -p /mnt/hugepages
echo "nodev hugetlbfs /mnt/hugepages hugetlbfs pagesize=2M 0 0" >> /etc/fstab
mount /mnt/hugepages
数据库场景计算方式:InnoDB Buffer Pool 16GB → 需要 8192 个 2MB 大页。建议预留 10% 余量防止碎片化。
2.2 OOM Killer 调优防护
当系统内存耗尽时,OOM Killer 根据 oom_score 杀掉得分最高的进程。关键防护策略:
/proc/$(pidof postmaster)/oom_score_adj
# Docker 容器 OOM 权重
docker run --oom-score-adj=-500 ...
# 全局禁用 OOM killer(不推荐,触发 panic)
sysctl vm.panic_on_oom=1
2.3 swap 与缓存策略
vm.swappiness 控制内核回收内存时 swap 的积极程度:
- 数据库服务器(Redis/PostgreSQL):swappiness=1,尽可能避免 swap,保留 LRU 活跃页
- 通用 Web 应用:swappiness=10,平衡缓存与 swap 压力
- 容器平台主机:swappiness=0,依赖 cgroup memory limit 限制
三、I/O 子系统与块层调优
3.1 I/O 调度器选型
Linux 提供多种块设备 I/O 调度器,根据硬件和工作负载选择:
| 调度器 | 适用场景 | 特点 |
|---|---|---|
| none (Noop) | NVMe SSD、虚拟化 | 无排序开销,直接提交 |
| mq-deadline | 数据库(MySQL/PG) | 读写分离,读优先保障延迟 |
| bfq | 桌面/交互式应用 | 按进程分配带宽,公平性强 |
| kyber | 高速随机存储 | 自适应调节,目标延迟可配 |
/sys/block/nvme0n1/queue/scheduler
# 永久规则(udev)
echo 'ACTION=="add|change", KERNEL=="nvme[0-9]", ATTR{queue/scheduler}="none"' \
>> /etc/udev/rules.d/60-io-scheduler.rules
3.2 I/O 参数深度调优
/sys/block/nvme0n1/queue/nr_requests
# 预读大小优化(顺序扫描场景 4096KB,随机 OLTP 256KB)
echo 4096 > /sys/block/sda/queue/read_ahead_kb
# 禁用合并调度器统计开销(高性能场景)
echo 0 > /sys/block/sda/queue/io_poll
echo 2 > /sys/block/sda/queue/rq_affinity # 完成 CPU 与提交 CPU 相同
3.3 文件系统选型与挂载参数
ext4 与 XFS 的性能差异及生产推荐:
四、cgroup v2 资源隔离实战
4.1 cgroup v2 vs v1 架构差异
cgroup v2 统一了层级树结构,解决了 v1 各子系统策略不一致的问题。核心改进包括:
- 统一 hierarchy:所有资源控制器挂载在同一树形结构上
- 线程级资源控制(Thread Mode):支持对线程组精细化管理
- 压力阻塞信息(PSI):提供精确的资源竞争度量
- 递归资源统计:子树资源使用自动向上汇聚
4.2 CPU 资源限制实操
/sys/fs/cgroup/app_group/cpu.max
# 格式:$MAX $PERIOD
# 表示在 100000μs 周期内最多使用 200000μs,即 2 核全功率
# 限制为 0.5 核
echo "50000 100000" > /sys/fs/cgroup/app_group/cpu.max
# 向 control group 添加进程
echo $PID > /sys/fs/cgroup/app_group/cgroup.procs
# 验证限制生效
cat /sys/fs/cgroup/app_group/cpu.stat
# 输出: usage_usec 123456789
# user_usec 98765432
# system_usec 24691357
# nr_periods 1000
# nr_throttled 123
# throttled_usec 12345678
关键观察指标:nr_throttled 表示被限流的周期数,throttled_usec 是总限流时间。如果 nr_throttled 持续增长,说明 CPU 限制过紧需要调整。
4.3 内存限制与分层控制
/sys/fs/cgroup/app_group/memory.max
# 设置软限制 3GB(内存紧张时优先回收此组)
echo "3G" > /sys/fs/cgroup/app_group/memory.high
# 设置内核内存上限(防止内核对象爆炸)
echo "256M" > /sys/fs/cgroup/app_group/memory.kmem.limit_in_bytes
# 查看 OOM 事件
cat /sys/fs/cgroup/app_group/memory.events
# low 0, high 12, max 0, oom 0, oom_kill 0
# 内存用量详情
cat /sys/fs/cgroup/app_group/memory.current
# 3221225472 (约 3 GB)
4.4 Block I/O 带宽限速
/sys/fs/cgroup/app_group/io.max
# 限制 IOPS:读 5000,写 2000
echo "8:0 riops=5000 wiops=2000" > /sys/fs/cgroup/app_group/io.max
# 查看 I/O 统计
cat /sys/fs/cgroup/app_group/io.stat
# 8:0 rbytes=123456789 wbytes=98765432 rios=12345 wios=6789
4.5 PSI(Pressure Stall Information)实战
PSI 提供了精确的资源竞争度量,比传统负载平均值更直接反映问题:
/sys/fs/cgroup/app_group/memory.pressure
# 含义:如果在 100ms 窗口内,超过 500ms 的任务等待时间,触发通知
五、网络子系统调优
5.1 核心参数调优
/etc/sysctl.d/99-network-tuning.conf # 核心网络缓冲区 net.core.rmem_max = 134217728 net.core.wmem_max = 134217728 net.core.rmem_default = 16777216 net.core.wmem_default = 16777216 net.core.optmem_max = 16777216 net.core.netdev_max_backlog = 50000 # TCP 全局参数 net.ipv4.tcp_rmem = 4096 16777216 134217728 net.ipv4.tcp_wmem = 4096 16777216 134217728 net.ipv4.tcp_max_syn_backlog = 65536 net.ipv4.tcp_max_tw_buckets = 262144 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_slow_start_after_idle = 0 net.ipv4.tcp_mtu_probing = 1 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_sack = 1 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_no_metrics_save = 1 # epoll 连接跟踪 net.ipv4.tcp_max_orphans = 262144 sunrpc.tcp_max_slot_table_entries = 128 net.ipv4.tcp_syncookies = 1
5.2 网卡多队列与 XDP 加速
/sys/class/net/eth0/queues/rx-0/rps_cpus
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
# XDP 程序加载(DPDK 替代方案,内核原生加速)
ip link set eth0 xdpgeneric obj xdp_drop.o
六、perf 与 eBPF 性能分析
6.1 perf 工具链实战
cpu-flame.svg
6.2 eBPF/BCC 工具链
6.3 bpftrace 一行命令
] = count(); }'
# 追踪所有阻塞的 I/O(I/O 问题诊断)
bpftrace -e 'kprobe:blk_account_io_start { @[comm, args->rq->rq_disk->disk_name] = count(); }'
# 监控 OOM killer 触发事件
bpftrace -e 'kprobe:oom_kill_process { printf("OOM kill: %s pid %d\n", comm, pid); }'
# 追踪 TCP 三次握手失败
bpftrace -e 'kprobe:tcp_v4_conn_request { printf("port %d, state %d\n", args->sk->sk_num, args->sk_state); }'
七、监控告警体系构建
7.1 关键指标体系
性能监控应覆盖以下黄金信号:
| 指标类别 | 关键指标 | 健康阈值 | 告警阈值 |
|---|---|---|---|
| CPU | load avg / core 数 | < 0.7 | > 1.0 (5min) |
| CPU | %steal (虚拟化) | < 2% | > 5% |
| 内存 | Available / Total | > 20% | < 10% |
| 内存 | PSI some > 5% | < 1% | > 5% (持续 1min) |
| I/O | %util | < 60% | > 85% |
| I/O | avgqu-sz | < 10 | > 50 |
| 网络 | retrans_rate | < 0.01% | > 0.1% |
7.2 一键健康检查脚本
#!/bin/bash
# system-health-check.sh
echo "=== CPU Health ==="
LOAD=$(cat /proc/loadavg | cut -d' ' -f1-3)
CORES=$(nproc)
echo "Load: $LOAD Cores: $CORES"
echo "Per-core load: $(echo "$LOAD" | awk -v c=$CORES '{printf "%.2f", $1/c}')"
echo -e "\n=== Memory Health ==="
AVAIL=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
TOTAL=$(grep MemTotal /proc/meminfo | awk '{print $2}')
PCT=$((AVAIL * 100 / TOTAL))
echo "Available: ${AVAIL}kB / ${TOTAL}kB (${PCT}%)"
echo -e "\n=== I/O Pressure ==="
for disk in $(lsblk -nd --output NAME | grep -E "^(sd|nvme|vd)"); do
UTIL=$(iostat -d -x $disk 1 2 | tail -n1 | awk '{print $NF}')
echo " $disk util: ${UTIL}%"
done
echo -e "\n=== PSI Memory ==="
grep "some" /proc/pressure/memory | awk '{printf " 10s:%.2f%% 60s:%.2f%% 300s:%.2f%%\n", $2, $3, $4}'
echo -e "\n=== TCP Retrans ==="
grep "segments retransmited" /proc/net/snmp 2>/dev/null || \
netstat -s | grep -i retrans
八、生产环境最佳实践清单
以下是从无数运维事故中总结的调优原则:
- 每次只改一个变量:性能调优是科学实验,控制变量才能定位效果
- 先测量,后优化:没有性能剖析(profile)数据的调优都是猜测
- 关注瓶颈转移:CPU 优化后瓶颈可能转到 I/O,需全链路审视
- 压测验证生产等效:使用真实流量影子(shadow traffic)或 tc/netem 模拟
- 内核版本要新:5.15+ 的调度器和内存管理比 3.x 有质的飞跃
- 禁用不需要的服务:关闭 GUI、CUPS、蓝牙等节省资源
- NUMA 感知:大内存应用务必 numactl 绑定
- cgroup 优先:容器化是趋势,资源隔离从 cgroup 入手
- 监控先行:没有 Prometheus + Grafana + Node Exporter 监控的系统不上线
- 文档化所有变更:/etc/sysctl.d/ 下每个 .conf 都要有注释说明理由
总结
Linux 性能调优是一个系统工程,涉及 CPU、内存、I/O、网络多个子系统的协同工作。理解底层机制(CFS、NUMA、页表、块层调度器)是做出正确调优决策的前提。在实际生产中,建议遵循"测量 → 分析 → 调优 → 验证"的闭环流程,避免盲目套用配置模板。随着 eBPF 工具的成熟,我们拥有了前所未有的系统可观测性——善用这些工具,让数据驱动决策。

发表评论 取消回复