linux shell

关注公众号 jb51net

关闭
首页 > 脚本专栏 > linux shell > Linux Shell脚本监控CPU使用率

通过Shell脚本监控Linux系统的CPU使用率

作者:Jinkxs

在现代运维与开发工作中,实时监控系统资源使用情况是保障服务稳定性的关键一环,CPU 作为计算机的核心处理单元,其使用率直接影响系统的响应速度和效率,本文将深入探讨如何通过 Shell 脚本监控 Linux 系统的 CPU 使用率,需要的朋友可以参考下

前言

在现代运维与开发工作中,实时监控系统资源使用情况是保障服务稳定性的关键一环。CPU 作为计算机的核心处理单元,其使用率直接影响系统的响应速度和任务执行效率。本文将深入探讨如何通过 Shell 脚本监控 Linux 系统的 CPU 使用率,并结合 Java 实现跨平台的数据采集、报警机制与可视化展示。

无论你是 DevOps 工程师、后端开发者,还是正在学习 Linux 系统管理的学生,这篇内容都将为你提供实用的脚本编写技巧、性能优化思路以及工程化实践方法。我们不仅会教你写一个基础监控脚本,还会带你构建完整的监控体系 —— 包括定时任务、日志记录、阈值告警、数据持久化和图形化报表。

为什么需要监控 CPU 使用率?

在生产环境中,CPU 使用率飙升往往是系统故障或性能瓶颈的前兆。例如:

通过主动监控 CPU 使用率,我们可以:

✅ 提前发现异常负载
✅ 快速定位问题根源
✅ 自动触发告警或重启机制
✅ 为容量规划提供数据支持

小贴士:CPU 使用率 ≠ 系统负载(Load Average)。前者反映 CPU 时间片占用比例,后者体现系统排队任务数量。二者需结合分析。

Linux 下获取 CPU 使用率的方法

Linux 提供了多种方式查看 CPU 使用情况,最常用的是 /proc/stat 文件和 top/htop 命令。我们推荐使用 /proc/stat,因为它原始、高效、无依赖,适合脚本自动化采集。

/proc/stat 文件结构解析

cat /proc/stat

输出示例:

cpu  123456 789 101112 131415 161718 192021 222324 0 0
cpu0 65432 394 50556 65707 80859 96010 111161 0 0
...

第一行 cpu 表示所有核心的汇总数据,各字段含义如下(单位:jiffies,通常 1 jiffy = 10ms):

字段含义
user用户态时间(普通进程)
nice低优先级用户态时间
system内核态时间
idle空闲时间
iowaitI/O 等待时间
irq硬中断时间
softirq软中断时间
steal虚拟机被宿主机抢占时间
guest运行虚拟 CPU 的时间

注意:部分系统可能只有前 4 个字段,新内核支持更多字段。脚本应具备兼容性。

编写基础 Shell 监控脚本

下面是一个简单的 Shell 脚本,每 5 秒采集一次 CPU 使用率:

#!/bin/bash

# cpu_monitor.sh - 基础版 CPU 使用率监控脚本
# 作者:SystemAdmin
# 日期:2025

INTERVAL=5

get_cpu_usage() {
    # 读取 /proc/stat 第一行
    read cpu user nice system idle iowait irq softirq steal guest < /proc/stat
    
    # 计算总时间和空闲时间
    total=$((user + nice + system + idle + iowait + irq + softirq + steal))
    idle_time=$idle

    sleep $INTERVAL

    # 再次读取
    read cpu user2 nice2 system2 idle2 iowait2 irq2 softirq2 steal2 guest2 < /proc/stat
    total2=$((user2 + nice2 + system2 + idle2 + iowait2 + irq2 + softirq2 + steal2))
    idle_time2=$idle2

    # 计算差值
    total_diff=$((total2 - total))
    idle_diff=$((idle_time2 - idle_time))

    # 避免除零
    if [ $total_diff -eq 0 ]; then
        echo "0.0"
        return
    fi

    # 计算使用率: (总时间 - 空闲时间) / 总时间 * 100
    usage=$(awk "BEGIN {printf \"%.2f\", (1 - $idle_diff / $total_diff) * 100}")
    echo "$usage"
}

echo "开始监控 CPU 使用率... (按 Ctrl+C 退出)"
while true; do
    usage=$(get_cpu_usage)
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] CPU 使用率: ${usage}%"
done

保存为 cpu_monitor.sh,赋予执行权限:

chmod +x cpu_monitor.sh
./cpu_monitor.sh

引入 Mermaid 图表展示趋势

为了更直观地理解 CPU 使用率的变化趋势,我们可以在文档中嵌入动态图表。虽然 Shell 脚本本身无法直接渲染图表,但我们可以将数据输出为 CSV 或 JSON,再配合前端工具展示。

以下是一个模拟过去 10 分钟 CPU 使用率变化的 Mermaid 折线图:

渲染错误: Mermaid 渲染失败: No diagram type detected matching given configuration for text: lineChart title 过去10分钟CPU使用率趋势 (%) x-axis 时间点 --> 0,1,2,3,4,5,6,7,8,9,10 y-axis 使用率 --> 0,20,40,60,80,100 series "服务器A" --> 15,22,30,65,78,85,92,88,75,60,45 series "服务器B" --> 10,18,25,30,35,40,45,50,48,42,38

图表说明:横轴为采样时间点(每分钟一次),纵轴为 CPU 使用率百分比。可清晰看到服务器 A 在第 6 分钟出现峰值,需进一步排查。

增强版脚本:带日志记录与阈值告警

下面我们升级脚本,加入:

#!/bin/bash
# advanced_cpu_monitor.sh - 增强版 CPU 监控脚本
# 支持日志、告警、参数配置
LOG_FILE="cpu_monitor.log"
MAX_LOG_SIZE=10485760  # 10MB
ALERT_THRESHOLD=${1:-80}   # 默认阈值 80%
INTERVAL=${2:-5}           # 默认间隔 5 秒
rotate_log() {
    if [ -f "$LOG_FILE" ] && [ $(stat -c%s "$LOG_FILE") -gt $MAX_LOG_SIZE ]; then
        mv "$LOG_FILE" "${LOG_FILE}.old"
        echo "[$(date)] 日志文件已轮转" >> "${LOG_FILE}.old"
    fi
}
alert_if_high() {
    local usage=$1
    if (( $(echo "$usage > $ALERT_THRESHOLD" | bc -l) )); then
        echo "⚠️  [$(date)] 警告:CPU 使用率过高!当前:${usage}%,阈值:${ALERT_THRESHOLD}%" >&2
    fi
}
get_cpu_usage() {
    read cpu user nice system idle iowait irq softirq steal guest < /proc/stat
    total=$((user + nice + system + idle + iowait + irq + softirq + steal))
    idle_time=$idle
    sleep $INTERVAL
    read cpu user2 nice2 system2 idle2 iowait2 irq2 softirq2 steal2 guest2 < /proc/stat
    total2=$((user2 + nice2 + system2 + idle2 + iowait2 + irq2 + softirq2 + steal2))
    idle_time2=$idle2
    total_diff=$((total2 - total))
    idle_diff=$((idle_time2 - idle_time))
    if [ $total_diff -eq 0 ]; then
        echo "0.0"
        return
    fi
    usage=$(awk "BEGIN {printf \"%.2f\", (1 - $idle_diff / $total_diff) * 100}")
    echo "$usage"
}
echo "🚀 启动增强版 CPU 监控..."
echo "📊 阈值:${ALERT_THRESHOLD}%,间隔:${INTERVAL}秒"
while true; do
    rotate_log
    usage=$(get_cpu_usage)
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    alert_if_high "$usage"
    log_entry="[$timestamp] CPU: ${usage}%"
    echo "$log_entry" | tee -a "$LOG_FILE"
    # 可选:写入单独的 CSV 文件用于后续分析
    echo "$timestamp,$usage" >> cpu_history.csv
done

运行示例:

./advanced_cpu_monitor.sh 85 10  # 设置阈值85%,间隔10秒

Java 实现跨平台 CPU 监控器

Shell 脚本虽强大,但在复杂业务场景下,我们往往需要更灵活的编程语言来实现:

下面是一个基于 Java 的 CPU 监控器,使用 OSHI 库(Operating System and Hardware Information)实现跨平台采集。

添加 Maven 依赖

<dependency>
    <groupId>com.github.oshi</groupId>
    <artifactId>oshi-core</artifactId>
    <version>6.4.8</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.14</version>
</dependency>

OSHI 官网:https://github.com/oshi/oshi (注:此处仅为说明用途,实际文章中不出现 GitHub 地址)

Java 核心监控类

import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class CPUMonitor {
    private static final Logger logger = LoggerFactory.getLogger(CPUMonitor.class);
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private final CentralProcessor processor;
    private final int intervalSeconds;
    private final double alertThreshold;
    private ScheduledExecutorService scheduler;
    public CPUMonitor(int intervalSeconds, double alertThreshold) {
        this.intervalSeconds = intervalSeconds;
        this.alertThreshold = alertThreshold;
        SystemInfo si = new SystemInfo();
        HardwareAbstractionLayer hal = si.getHardware();
        this.processor = hal.getProcessor();
    }
    public void start() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(this::collectAndLog, 0, intervalSeconds, TimeUnit.SECONDS);
        logger.info("✅ CPU 监控器已启动,间隔:{}秒,告警阈值:{}%", intervalSeconds, alertThreshold);
    }
    public void stop() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            logger.info("⏹️  CPU 监控器已停止");
        }
    }
    private void collectAndLog() {
        double[] load = processor.getProcessorCpuLoadBetweenTicks();
        double usage = load[0] * 100; // 获取整体 CPU 使用率
        String timestamp = LocalDateTime.now().format(formatter);
        String logMessage = String.format("[%s] CPU 使用率: %.2f%%", timestamp, usage);
        if (usage > alertThreshold) {
            logger.warn("🚨 高负载警告!{}", logMessage);
            triggerAlert(usage);
        } else {
            logger.info(logMessage);
        }
    }
    private void triggerAlert(double usage) {
        // 此处可扩展:发送邮件、调用 webhook、写入数据库等
        System.err.println("📢 触发告警逻辑:CPU 使用率 " + String.format("%.2f", usage) + "% 超过阈值 " + alertThreshold + "%");
    }
    public static void main(String[] args) {
        int interval = args.length > 0 ? Integer.parseInt(args[0]) : 5;
        double threshold = args.length > 1 ? Double.parseDouble(args[1]) : 80.0;
        CPUMonitor monitor = new CPUMonitor(interval, threshold);
        Runtime.getRuntime().addShutdownHook(new Thread(monitor::stop));
        monitor.start();
        // 保持主线程运行
        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("主线程被中断", e);
        }
    }
}

配置 Logback 输出到文件

创建 src/main/resources/logback.xml

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/cpu_monitor.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/cpu_monitor.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="FILE"/>
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

编译并运行:

mvn compile
mvn exec:java -Dexec.mainClass="CPUMonitor" -Dexec.args="5 85"

Shell 与 Java 协同工作架构

在实际项目中,我们常采用“Shell + Java”混合架构:

下面是一个协同架构示意图:

渲染错误: Mermaid 渲染失败: Lexical error on line 2. Unrecognized text. ...|每5秒采集| B[/proc/stat] B --> C{CPU 使用 -----------------------^

这种架构的优势:

🔹 Shell 脚本资源占用极低,适合高频采集
🔹 Java 服务可横向扩展,处理复杂业务逻辑
🔹 解耦设计,便于独立部署与维护

数据持久化与可视化

采集到的数据若不存储和展示,价值将大打折扣。我们可以将数据写入时序数据库(如 InfluxDB),再通过 Grafana 展示仪表盘。

示例:Java 写入 InfluxDB

添加依赖:

<dependency>
    <groupId>com.influxdb</groupId>
    <artifactId>influxdb-client-java</artifactId>
    <version>7.0.0</version>
</dependency>

修改 triggerAlert 方法:

private InfluxDBClient influxDBClient;
private String bucket = "system_metrics";
private String org = "myorg";
public CPUMonitor(int intervalSeconds, double alertThreshold) {
    // ... 原有代码
    initInfluxDB();
}
private void initInfluxDB() {
    influxDBClient = InfluxDBClientFactory.create(
        "http://localhost:8086",
        "your-token".toCharArray(),
        org,
        bucket
    );
}
private void writeMetric(double usage) {
    Point point = Point.measurement("cpu_usage")
        .addTag("host", "server-01")
        .addField("value", usage)
        .time(Instant.now(), WritePrecision.NS);
    influxDBClient.getWriteApiBlocking().writePoint(point);
    logger.debug("📈 数据已写入 InfluxDB: {}%", usage);
}

🔗 Grafana 官网:https://grafana.com/
🔗 InfluxDB 文档:https://docs.influxdata.com/influxdb/

高级功能:智能基线告警

固定阈值告警容易误报(如夜间低峰期 vs 白天高峰期)。更好的做法是建立“动态基线”。

我们可以:

  1. 记录历史同期数据(如过去7天同一时刻)
  2. 计算均值 + 标准差
  3. 当前值 > 均值 + 2×标准差 时触发告警

Java 实现伪代码:

public class BaselineAlertManager {
    private Map<String, List<Double>> historyData = new ConcurrentHashMap<>();
    public boolean shouldAlert(String timeKey, double currentValue) {
        List<Double> pastValues = historyData.getOrDefault(timeKey, new ArrayList<>());
        if (pastValues.size() < 5) {
            pastValues.add(currentValue);
            historyData.put(timeKey, pastValues);
            return false; // 数据不足,暂不告警
        }
        double mean = pastValues.stream().mapToDouble(v -> v).average().orElse(0.0);
        double stdDev = calculateStandardDeviation(pastValues, mean);
        double baseline = mean + 2 * stdDev;
        if (currentValue > baseline) {
            logger.warn("📈 动态基线告警:当前 {}%,基线 {}%", currentValue, baseline);
            return true;
        }
        // 更新历史数据(滑动窗口)
        if (pastValues.size() >= 30) {
            pastValues.remove(0);
        }
        pastValues.add(currentValue);
        return false;
    }
    private double calculateStandardDeviation(List<Double> values, double mean) {
        double sum = values.stream()
            .mapToDouble(v -> Math.pow(v - mean, 2))
            .sum();
        return Math.sqrt(sum / values.size());
    }
}

性能测试与优化建议

任何监控系统自身不应成为性能负担。以下是优化要点:

Shell 脚本优化

Java 应用优化

移动端告警推送示例

当 CPU 异常时,可通过企业微信、钉钉或 Telegram 发送通知。

以企业微信为例(Java):

public class WeComNotifier {
    private static final String WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY";
    public static void sendAlert(String message) {
        String jsonPayload = String.format("""
            {
              "msgtype": "text",
              "text": {
                "content": "%s",
                "mentioned_list": ["@all"]
              }
            }
            """, message);
        try {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(WEBHOOK_URL))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
                .build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() == 200) {
                logger.info("✅ 企业微信消息发送成功");
            } else {
                logger.error("❌ 发送失败: {}", response.body());
            }
        } catch (Exception e) {
            logger.error("发送企业微信消息异常", e);
        }
    }
}

企业微信 API 文档:https://work.weixin.qq.com/api/doc

扩展功能:监控多核 CPU 与进程级占用

除了整体 CPU 使用率,有时还需监控:

Shell 获取各核心使用率

#!/bin/bash
get_per_core_usage() {
    cores=()
    while IFS= read -r line; do
        if [[ $line =~ ^cpu[0-9]+ ]]; then
            cores+=("$line")
        fi
    done < /proc/stat
    for core_line in "${cores[@]}"; do
        read -r core_name user nice system idle iowait irq softirq steal <<< "$core_line"
        total=$((user + nice + system + idle + iowait + irq + softirq + steal))
        idle_time=$idle
        # 存储初始值(略)...
        # 之后计算每个核心的使用率(逻辑同整体 CPU)
        # ...
    done
}

Java 获取指定进程 CPU 占用

OSHI 也支持按进程统计:

import oshi.software.os.OSProcess;
// ...
for (OSProcess p : si.getOperatingSystem().getProcesses(5, ProcessSort.CPU_DESC)) {
    if (p.getName().contains("java")) {
        double cpuUsage = p.getProcessCpuLoadCumulative() * 100;
        logger.info("Java 进程 [{}] CPU 占用: {:.2f}%", p.getProcessID(), cpuUsage);
    }
}

生产环境部署建议

将监控脚本投入生产前,请考虑以下事项:

1. 权限最小化原则

2. 高可用与自愈

3. 日志与审计

4. 容量规划

开源监控工具对比

虽然我们实现了自研监控,但在大型系统中,使用成熟开源工具往往更高效:

工具优势适用场景
Prometheus强大的查询语言,生态丰富云原生、K8s 环境
Zabbix企业级功能完整,支持自动发现传统 IDC、混合云
Telegraf轻量级采集器,插件丰富边缘设备、IoT
DatadogSaaS 服务,开箱即用中小型团队快速上手

🔗 Prometheus 官网:https://prometheus.io/
🔗 Zabbix 官网:https://www.zabbix.com/

自研监控的价值在于:

结语:监控是持续演进的过程

CPU 监控只是系统可观测性的冰山一角。真正的稳定性保障需要结合:

🔸 日志监控(Log Monitoring)
🔸 指标监控(Metrics Monitoring)
🔸 链路追踪(Distributed Tracing)
🔸 健康检查(Health Check)
🔸 自动化修复(Auto Remediation)

希望本文提供的 Shell 脚本与 Java 代码能为你构建监控体系打下坚实基础。记住:没有完美的监控,只有不断迭代的监控。随着业务增长,你的监控系统也应随之进化。

最后思考题:如何让监控系统在 CPU 100% 时仍能正常发送告警?欢迎留言讨论!

附录:相关 Linux 命令速查

# 查看实时 CPU 使用率
top -bn1 | grep "Cpu(s)"

# 查看每个核心使用率
mpstat -P ALL 1

# 查看进程 CPU 占用 TOP 10
ps aux --sort=-%cpu | head -11

# 查看系统平均负载
uptime

# 查看 CPU 详细信息
lscpu

Happy Monitoring! 

以上就是通过Shell脚本监控Linux系统的CPU使用率的详细内容,更多关于Linux Shell脚本监控CPU使用率的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文