跳转至

系统静默配置步骤总结

配置阶段 具体操作 目的 注意事项
一、启动参数配置 1. 在引导项中添加 isolcpus=managed_irq,7 2. 添加 nohz_full=7 3. 添加 rcu_nocbs=7 4. 添加 nowatchdog nmi_watchdog=0 5. 添加 hpet=disable 6. 添加 tsc=reliable 7. 添加 mce=off 8. 添加 ipv6.disable=1 9. 添加 audit=0 10. 添加 printk.devkmsg=off quiet loglevel=1 11. 添加selinux=0 1. 将核心7从通用调度器隔离,managed_irq 模式允许中断在该CPU上运行但阻止用户/内核线程抢占 2. 启用自适应无滴答模式,在idle时停止周期性时钟中断,显著降低抖动 3. 将RCU回调卸载到非隔离CPU,避免RCU softirq干扰关键线程 4. 禁用NMI watchdog,防止其产生不必要的定时器中断 5. 禁用HPET多播中断源,减少全局中断负载 6. 声明TSC为可靠时间源,避免内核频繁校准带来抖动 7. 关闭机器检查异常(MCE),避免不可屏蔽中断(NMI)打断执行 8. 减少IPv6协议栈相关的后台任务和中断处理 9. 禁用审计子系统,消除 auditd 和 audit backlog 处理带来的不确定性开销 10. 抑制内核日志输出,降低console和syslog写入引起的中断与竞争 11. 禁用 SE Linux 扩展 1. isolcpus 必须配合 nohz_full 才能实现真正的“静默”效果;建议使用 isolcpus=managed_irq,<cpu_list>而非旧格式 2. nohz_full 要求目标CPU不运行任何除0号进程外的任务,否则会退化回有滴答模式,确保内核中配置了CONFIG_NO_HZ_FULL=y,可以通过 `cat /boot/config-*
二、中断与任务隔离 1. 执行命令:systemctl stop irqbalance && systemctl disable irqbalance 2. 手动绑定所有非关键设备中断(如网卡、磁盘)至非目标CPU(如CPU 0-6): echo <cpu_id> > /proc/irq/<irq_num>/smp_affinity_list 3. 编辑 /sys/devices/virtual/workqueue/cpumask,设置为 0x7F(即 CPU 0-6) 4. (可选)将 ksoftirqd/7 进程手动迁移: taskset -pc 0 $(pgrep ksoftirqd/7) 1. 防止 irqbalance自动将中断迁移到隔离核心 2. 主动控制IRQ亲和性,确保硬件中断不会落入核心7 3. 限制所有通用工作队列(workqueue)仅在非隔离CPU上运行,防止内核worker抢占 4. 强制将软中断守护进程移出核心7,进一步降低潜在干扰风险 1. 禁用 irqbalance 后必须定期检查 /proc/interrupts,防止新设备中断误绑 2. 使用 smp_affinity_list接口比位掩码更直观且不易出错 3. 修改 workqueue/cpumask 需 root 权限,并注意系统更新后可能重置 4. ksoftirqd迁移是临时手段,若未结合 nohz_full效果有限
三、电源管理优化 1. BIOS 中禁用: - Turbo Boost - C-States (C1E, C-State Control) - P-States / SpeedStep - SMT/Hyper-Threading(可选) 2. 内核启动参数添加: intel_pstate=disable processor.max_cstate=1 intel_idle.max_cstate=1 idle=poll 3. 运行时设置频率策略: sudo cpupower frequency-set -g performance 4. 锁定频率(可选): sudo cpupower frequency-set -f <target_freq>MHz 5. 加载 msr 模块并使用 WRMSR 工具固定 UNCORE 频率: modprobe msr wrmsr -p7 0x620 <min_max_ratio> 1. 消除动态调频(P-State)、自动超频(Turbo)和深度睡眠(C-State)导致的频率波动与唤醒延迟 2. 将CPU保持在浅层C1状态或强制轮询(idle=poll),实现最低延迟响应 3. 确保CPU始终运行于最高稳定频率,避免performance governor切换延迟 4. 固定频率避免dvfs过渡态引入抖动 5. 控制Uncore(LLC、内存控制器)频率一致性,避免跨NUMA带宽波动 1. BIOS设置需物理访问服务器或远程KVM操作 2. idle=poll极大增加功耗,仅适用于短期压测或低延迟场景 3. cpupower工具链需提前安装(如 linux-tools-common) 4. MSR操作需谨慎,错误值可能导致系统不稳定或降频
四、其他关键优化 1. 禁用VT-x虚拟化支持(BIOS);避免跨核读写MSR寄存器;降低RDTSC采样频率 2. 使用 chrt 设置实时优先级: chrt -f 99 ./realtime_app 3.echo -1 > /proc/sys/kernel/sched_rt_runtime_us将实时任务的CPU使用时间限制设为无限制 4. NUMA绑定:使用 numactl将进程、内存、I/O设备绑定到同一节点: numactl --cpunodebind=0 --membind=0 --physcpubind=7 ./app 同时将I/O中断亲和性设为另一NUMA节点CPU 5. 检测SMI(System Management Interrupt)开销: Intel: perf stat --smi-cost sleep 10 AMD: perf stat -e ls_smi_rx -I 10000 sleep 1 存在高频SMI时调整BIOS中SMM相关选项 6. 禁用透明大页THP: 方式一:transparent_hugepage=never(boot参数) 方式二:echo never > /sys/kernel/mm/transparent_hugepage/enabled 7. 在计时代码前后插入串行化指令: asm volatile("cpuid" ::: "eax", "ebx", "ecx", "edx"); 再执行 rdtsc 或 使用rdtscp 1. 减少IPI、VMX相关陷阱及MSR访问引发的微小延迟抖动 2. 提升进程调度优先级,使其能立即抢占普通任务 3. 允许实时任务(RT任务)不受限制使用CPU资源 4. 实现计算、内存、I/O局部性,避免跨NUMA延迟与带宽瓶颈 5. SMI为最高优先级中断,无法被屏蔽,必须通过BIOS调优规避 6. THP后台合并线程(khugepaged)会产生不可预测的延迟尖峰 7. CPUID作为内存屏障,防止RDTSC乱序执行,保证时间戳准确性,RDTSCP会保证前面的指令不会乱序执行到RDTSCP后方 1. 禁用VT-x会影响容器、KVM等虚拟化功能 2. chrt -f 99若滥用会导致系统无响应,应仅用于单个关键进程 3. 若RT任务出现死循环,可能导致系统完全无响应 4. NUMA绑定需结合 numactl --show验证实际绑定结果 5. SMI调优依赖具体主板/BMC固件,常见选项包括:USB SMI、Legacy USB Support、PCI Lock等 6. THP禁用后可能影响数据库类应用性能,需权衡场景 7. CPUID带来约50~100 cycle开销,适合高精度测量而非高频采样

Linux平台静默工具类

#pragma once

#include <pthread.h>
#include <sched.h>
#include <sys/types.h>
#include <unistd.h>

#include <algorithm>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cstdint>
#include <fstream>
#include <sstream>
#include <string>
#include <string_view>
#include <thread>
#include <vector>

/// 进程优先级等级
enum class PriorityLevel {
  LowPriority = -1,    ///< 低优先级
  NormalPriority = 0,  ///< 普通优先级
  HighPriority,        ///< 高优先级
  RealtimePriority     ///< 实时优先级
};

/**
 * @brief Linux平台进程和线程辅助类
 */
class process_helper {
 public:
  static inline uint32_t get_pid() noexcept {
    return static_cast<uint32_t>(::getpid());
  }

  static inline bool set_priority(PriorityLevel prio) noexcept {
    const int max_prio = ::sched_get_priority_max(SCHED_FIFO);
    if (max_prio == -1) return false;

    int value;
    switch (prio) {
      case PriorityLevel::RealtimePriority:
        value = max_prio;
        break;
      case PriorityLevel::HighPriority:
        value = max_prio / 2;
        break;
      case PriorityLevel::NormalPriority:
        value = std::max(1, max_prio / 3);
        break;
      case PriorityLevel::LowPriority:
        value = 1;
        break;
      default:
        return false;
    }

    ::sched_param param{value};
    return ::sched_setscheduler(0, SCHED_FIFO, &param) == 0;
  }

  static inline bool thread_bind_core(uint32_t cpu) noexcept {
    const auto hw_concur = std::thread::hardware_concurrency();
    const uint32_t ncpus =
        hw_concur == 0 ? 1 : static_cast<uint32_t>(hw_concur);
    if (cpu >= ncpus) return false;

    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(cpu, &mask);
    return ::pthread_setaffinity_np(::pthread_self(), sizeof(mask), &mask) == 0;
  }

  static inline bool set_thread_priority(PriorityLevel prio) noexcept {
    const int max_prio = ::sched_get_priority_max(SCHED_FIFO);
    if (max_prio == -1) return false;

    int value;
    switch (prio) {
      case PriorityLevel::RealtimePriority:
        value = max_prio;
        break;
      case PriorityLevel::HighPriority:
        value = max_prio / 2;
        break;
      case PriorityLevel::NormalPriority:
        value = std::max(1, max_prio / 3);
        break;
      case PriorityLevel::LowPriority:
        value = 1;
        break;
      default:
        return false;
    }

    ::sched_param param{value};
    return ::pthread_setschedparam(::pthread_self(), SCHED_FIFO, &param) == 0;
  }

  /**
   * @brief 获取设备对应的所有 IRQ 号(支持多队列网卡)
   * @return vector<int>,可能为空
   */
  static std::vector<int> get_irqs_by_device(std::string_view device) {
    std::vector<int> irqs;
    std::ifstream file("/proc/interrupts");
    if (!file) return irqs;

    std::string line;
    while (std::getline(file, line)) {
      if (line.empty() || line[0] == ' ') continue;  // 跳过表头或无效行

      // 查找第一个冒号前的 IRQ 号
      size_t colon = line.find(':');
      if (colon == std::string::npos) continue;

      std::string_view irq_part(line.data(), colon);
      // 跳过前导空格
      size_t start = 0;
      while (start < irq_part.size() && std::isspace(irq_part[start])) ++start;
      if (start >= irq_part.size()) continue;

      char* end_ptr = nullptr;
      errno = 0;
      long irq = std::strtol(std::string(irq_part.substr(start)).c_str(),
                             &end_ptr, 10);

      if (errno != 0 ||
          end_ptr == std::string(irq_part.substr(start)).c_str() ||
          irq < INT_MIN || irq > INT_MAX) {
        continue;
      }

      // 检查行尾是否包含设备名(精确匹配或 [device] 形式)
      std::string_view tail(line.data() + colon + 1, line.size() - colon - 1);
      if (contains_device(tail, device)) {
        irqs.push_back(static_cast<int>(irq));
      }
    }
    return irqs;
  }

  /**
   * @brief 绑定设备的所有 IRQ 到指定 CPU(适用于多队列设备)
   */
  static bool bind_device_irqs_to_cpu(std::string_view device, uint32_t cpu) {
    auto irqs = get_irqs_by_device(device);
    if (irqs.empty()) return false;

    bool success = true;
    for (int irq : irqs) {
      if (!bind_irq_to_cpu(irq, cpu)) success = false;
    }
    return success;
  }

 private:
  static bool contains_device(std::string_view line, std::string_view target) {
    // 去除前导空格
    size_t pos = 0;
    while (pos < line.size() && std::isspace(line[pos])) ++pos;
    if (pos >= line.size()) return false;

    std::string_view tokens = line.substr(pos);
    // 按空格分割(简单状态机,避免构造 vector<string>)
    size_t start = 0;
    while (start < tokens.size()) {
      // 跳过空格
      while (start < tokens.size() && std::isspace(tokens[start])) ++start;
      if (start >= tokens.size()) break;

      size_t end = start;
      while (end < tokens.size() && !std::isspace(tokens[end])) ++end;
      std::string_view token = tokens.substr(start, end - start);

      // 移除尾部 '+'(如 eth0+)
      if (!token.empty() && token.back() == '+') {
        token = token.substr(0, token.size() - 1);
      }

      // 检查 [target] 形式
      if (token.size() >= target.size() + 2 && token[0] == '[' &&
          token[token.size() - 1] == ']') {
        std::string_view inner = token.substr(1, token.size() - 2);
        if (inner == target) return true;
      } else if (token == target) {
        return true;
      }

      start = end;
    }
    return false;
  }

  static bool bind_irq_to_cpu(int irq, uint32_t cpu) {
    const auto hw_concur = std::thread::hardware_concurrency();
    const uint32_t ncpus =
        hw_concur == 0 ? 1 : static_cast<uint32_t>(hw_concur);
    if (cpu >= ncpus) return false;

    // 构造十六进制掩码(无前缀,小写)
    char mask_str[18];  // 64位最大16字符 + '\0'
    int len = snprintf(mask_str, sizeof(mask_str), "%llx", 1ULL << cpu);
    if (len <= 0 || static_cast<size_t>(len) >= sizeof(mask_str)) return false;

    std::string path = "/proc/irq/" + std::to_string(irq) + "/smp_affinity";
    std::ofstream file(path, std::ios::out | std::ios::trunc);
    if (!file) return false;

    file.write(mask_str, len);
    file.put('\n');
    return file.good();
  }
};