Linux shell编程:监控进程CPU使用率并使用 perf 抓取高CPU进程信息

news2024/9/24 5:22:16

0. 概要

本文将介绍一个用于监控一组进程CPU使用率的Shell脚本,,当检测到某进程的CPU使用率超出阈值时,使用 perf 工具抓取该进程的详细信息。
本shell脚本为了能在普通嵌入式系统上运行做了妥协和优化。

1. shell脚本流程的简要图示:

在这里插入图片描述

2. perf介绍

perf 是 Linux 内核提供的一个强大性能分析工具,能够用于分析和调优系统性能。它支持多种事件类型,如CPU时钟、缓存命中/未命中、中断等。

在本脚本中,当某个进程的CPU使用率超过设定阈值(例如80%)时,会使用以下命令抓取该进程的详细性能数据:

perf record -F 99 -e cpu-clock -p $pid -g -o "perf-$process_name.data" -- sleep $perf_sleep_time
  • -F 99:以每秒99次的频率进行采样。
  • -e cpu-clock:采样的事件类型为CPU时钟周期。
  • -p $pid:指定要采样的进程ID。
  • -g:记录调用栈信息,帮助分析性能瓶颈。
  • -o "perf-$process_name.data":将采样数据输出到指定文件中。
  • -- sleep $perf_sleep_time:持续采样时间为10秒。

通过抓取高CPU使用率进程的详细性能数据,我们可以深入分析性能瓶颈,找出导致高CPU使用的原因,从而进行针对性的优化。

更多介绍请查看:
使用perf(火焰图)查看热点函数和系统调用最大延迟函数
如何使用perf 统计cpu和内存?

3. shell脚本详解

  1. 日志文件配置

    # Log file location
    LOGFILE="process_monitor.log"
    # Redirect standard input, output, and error to log file
    exec 1>>"$LOGFILE"
    exec 2>>"$LOGFILE"
    

    这部分代码配置日志文件,并将标准输入、输出和错误重定向到日志文件中。

  2. 后台运行检测

    # Check if the script is already running
    if [ "$1" != "background" ]; then
        "$0" background &
        exit 0
    fi
    

    这段代码用于检测脚本是否已经在后台运行,如果没有,则重新以后台模式启动自己。

  3. 初始化上次报告时间文件

    # Initialize last report time file
    last_report_time_file="last_report_time"
    touch "$last_report_time_file"
    

    初始化用于存储上次报告时间的文件。

  4. 获取CPU总时间的函数

    # Function to get the total CPU usage from /proc/stat
    get_total_cpu_time() {
        awk '/^cpu / {print $2 + $3 + $4 + $5 + $6 + $7 + $8}' /proc/stat
    }
    

    /proc/stat 文件中获取CPU总时间。

  5. 获取进程CPU时间的函数

    # Function to get the process CPU usage from /proc/[pid]/stat
    get_process_cpu_time() {
        pid=$1
        awk '{print $14 + $15 + $16 + $17}' /proc/$pid/stat
    }
    

    /proc/[pid]/stat 文件中获取指定进程的CPU时间。

  6. 计算进程CPU使用率的函数

    # Function to calculate CPU usage of a process
    calculate_cpu_usage() {
        pid=$1
        prev_process_time=$(get_process_cpu_time "$pid")
        prev_total_time=$(get_total_cpu_time)
        sleep 1
        process_time=$(get_process_cpu_time "$pid")
        total_time=$(get_total_cpu_time)
    
        process_delta=$((process_time - prev_process_time))
        total_delta=$((total_time - prev_total_time))
    
        cpu_usage=$((100 * process_delta / total_delta))
        echo $cpu_usage
    }
    

    计算指定进程的CPU使用率。

  7. 加载上次报告时间的函数

    # Function to load the last report time for a PID
    load_last_report_time() {
        pid=$1
        grep "^$pid=" "$last_report_time_file" | cut -d'=' -f2
    }
    

    从文件中加载上次报告时间。

  8. 保存上次报告时间的函数

    # Function to save the last report time for a PID
    save_last_report_time() {
        pid=$1
        time=$2
        sed -i "/^$pid=/d" "$last_report_time_file"
        echo "$pid=$time" >> "$last_report_time_file"
    }
    

    将上次报告时间保存到文件中。

  9. 进程监控列表

    # List of process names to monitor
    process_names="top systemd"
    

    定义需要监控的进程名称列表。

  10. 监控循环

 while true; do
    current_time=$(date +%s)
    for process_name in $process_names; do
      if [ -n "$DEBUG_ON" ]; then
          echo "Checking process: $process_name"
      fi
  
      # Find all matching process PIDs
      pids=$(ps aux | grep "$process_name" | grep -v grep | awk '{print $2}')
      for pid in $pids; do
          # Calculate CPU usage
          cpu_usage=$(calculate_cpu_usage "$pid")
          # Check if CPU usage exceeds $max_cpu_usage%
          if [ "$cpu_usage" -gt $max_cpu_usage ]; then
              echo "High CPU usage detected for process '$process_name' (PID: $pid): $cpu_usage%"
              # Load the last report time for this PID
              last_time=$(load_last_report_time "$pid")
              last_time=${last_time:-0}
              time_diff=$((current_time - last_time))
  
              # Check if the last report time is more than 60 seconds ago
              if [ "$time_diff" -ge 60 ]; then
                  echo "time_diff: $time_diff, perf record -F 99 -e cpu-clock -p $pid -g -o perf-$process_name.data -- sleep $perf_sleep_time"
                  ps -p "$pid" -o pid,ppid,cmd,%mem,%cpu >> "$LOGFILE"
                  perf record -F 99 -e cpu-clock -p $pid -g -o "perf-$process_name.data" -- sleep $perf_sleep_time
                  # Save the last report time for this PID
                  save_last_report_time "$pid" "$current_time"
  
                  # sleep for 1 second
                  sleep 1
              fi
          else
              if [ -n "$DEBUG_ON" ]; then
                  echo "CPU usage for process '$process_name' (PID: $pid): $cpu_usage%"
              fi
          fi
      done
  done
  done
  

这是主要的监控循环,定期检查指定进程的CPU使用率,并在超过阈值时使用 perf 抓取详细信息。

4. 完整脚本实现

以下是优化后的Shell脚本,适用于普通嵌入式系统:

#!/bin/sh

# This script monitors the CPU usage of a list of processes

DEBUG_ON=1
# Log file location
LOGFILE="process_monitor.log"

# Redirect standard input, output, and error to log file
exec 1>>"$LOGFILE"
exec 2>>"$LOGFILE"

# Check if the script is already running
if [ "$1" != "background" ]; then
    "$0" background &
    exit 0
fi

# Initialize last report time file
last_report_time_file="last_report_time"
touch "$last_report_time_file"

# Function to get the total CPU usage from /proc/stat
get_total_cpu_time() {
    awk '/^cpu / {print $2 + $3 + $4 + $5 + $6 + $7 + $8}' /proc/stat
}

# Function to get the process CPU usage from /proc/[pid]/stat
get_process_cpu_time() {
    pid=$1
    awk '{print $14 + $15 + $16 + $17}' /proc/$pid/stat
}

# Function to calculate CPU usage of a process
calculate_cpu_usage() {
    pid=$1
    prev_process_time=$(get_process_cpu_time "$pid")
    prev_total_time=$(get_total_cpu_time)
    sleep 1
    process_time=$(get_process_cpu_time "$pid")
    total_time=$(get_total_cpu_time)

    process_delta=$((process_time - prev_process_time))
    total_delta=$((total_time - prev_total_time))

    cpu_usage=$((100 * process_delta / total_delta))
    echo $cpu_usage
}

# Function to load the last report time for a PID
load_last_report_time() {
    pid=$1
    grep "^$pid=" "$last_report_time_file" | cut -d'=' -f2
}

# Function to save the last report time for a PID
save_last_report_time() {
    pid=$1
    time=$2
    sed -i "/^$pid=/d" "$last_report_time_file"
    echo "$pid=$time" >> "$last_report_time_file"
}

# List of process names to monitor
process_names="top systemd"


echo "Monitoring CPU usage for processes: $process_names"

# Perf sleep time
perf_sleep_time=10
max_cpu_usage=80

# Monitoring loop
while true; do
    current_time=$(date +%s)
    for process_name in $process_names; do
        if [ -n "$DEBUG_ON" ]; then
            echo "Checking process: $process_name"
        fi

        # Find all matching process PIDs
        # pids=$(ps | grep "$process_name" | grep -v grep | awk '{print $1}')
        pids=$(ps aux | grep "$process_name" | grep -v grep | awk '{print $2}')
        for pid in $pids; do
            # Calculate CPU usage
            cpu_usage=$(calculate_cpu_usage "$pid")
            # Check if CPU usage exceeds $max_cpu_usage%
            if [ "$cpu_usage" -gt $max_cpu_usage ]; then
                echo "High CPU usage detected for process '$process_name' (PID: $pid): $cpu_usage%"
                # Load the last report time for this PID
                last_time=$(load_last_report_time "$pid")
                last_time=${last_time:-0}
                time_diff=$((current_time - last_time))

                # Check if the last report time is more than 60 seconds ago
                if [ "$time_diff" -ge 60 ]; then
                    echo "time_diff: $time_diff, perf record -F 99 -e cpu-clock -p $pid -g -o perf-$process_name.data -- sleep $perf_sleep_time"
                    ps -p "$pid" -o pid,ppid,cmd,%mem,%cpu >> "$LOGFILE"
                    perf record -F 99 -e cpu-clock -p $pid -g -o "perf-$process_name.data" -- sleep $perf_sleep_time
                    # Save the last report time for this PID
                    save_last_report_time "$pid" "$current_time"

                    # sleep for 1 second
                    sleep 1
                fi
            else
                if [ -n "$DEBUG_ON" ]; then
                    echo "CPU usage for process '$process_name' (PID: $pid): $cpu_usage%"
                fi
            fi
        done
    done

done

通过这种方式,我们可以有效地监控嵌入式系统中高CPU使用率的进程,并通过 perf 工具获取详细的性能数据,帮助我们进行性能调优和问题排查。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1989620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring 中请求作用域的数据存储在 ThreadLocal 中还是 Spring 容器中?

微信中阅读,欢迎👏👏👏关注公众号:CodeFit 。 创作不易,如果你觉得这篇文章对您有帮助,请不要忘了 点赞、分享 和 关注,为我的 持续创作 提供 动力! 最近看到一个有趣的问题,Request Scope(请求作用域) 的数据是存储在 ThreadLocal 中,还是 Spring 容器中? 事…

前端(六):Vue组件库Element

一、引入 Element:是饿了吗团队研发,一套为开发者、设计师和产品经理准备的基于vue2.0的桌面端组件库。组件:组成网页的部件,例如超链接、按钮、图片、表格、表单、分页条等。官网:https://element.eleme.cn/#/zh-CN …

景芯SoC DDR子系统

Memory子系统主要由DDR Controller和DDR_PHY(含DDR_IO)两个部分组成。DDR Controller主要承担其它子系统(如CPU)与Memory子系统进行数据交互时的传输效率及调度,DDR_PHY主要负责数据交互过程中的传输速度。 DDR内存接口IP解决方案包括DDR控制…

Revit二次开发选择过滤器,SelectionFilter

过滤器分为选择过滤器与规则过滤器 规则过滤器可以看我之前写的这一篇文章: Revit二次开发在项目中给链接模型附加过滤器 选择过滤器顾名思义就是可以将选择的构件ID集合传入并加入到视图过滤器中,有一些场景需要对某些构件进行过滤选择,但是没有共同的逻辑规则进行筛选的情况…

健康管理系统

目录 第1章 系统概述 第2章 可行性研究 2.1 项目背景及意义 2.2 可行性研究 第3章 需求分析 3.1 功能性需求 3.2 非功能性需求 3.2.1 性能需求 第4章 总体设计 4.1 技术架构 4.2功能模块设计 第5章 详细设计 5.1 主页 5.2 写剧本杀 5.3 剧本杀分类管理 5.4 个人…

PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation

Abstract 通常情况下研究人员会把点云数据转换为规则的3D体素网格或图像集合。这导致数据不必要的庞大,所以本文引入了一种新型的神经网络,能很好的尊重点云的排列不变性,名称是pointnet,并且能够应用于分类、分割、场景解析等下…

大模型入门无敌!《大模型基础》教材发布,已开源!

浙江大学DAILY实验室毛玉仁研究员、高云君教授领衔撰写的《大模型基础》教材第一版付梓。 本书旨在针对大语言模型感兴趣的读者系统地讲解相关基础知识、介绍前沿技术。作者团队将认真严肃开源社区以及广大专家学者的建议,持续进行月度更新,致力打造易读…

SQLServer Manager Studio扩展开发从入门到弃坑(针对17 ,18 。19)

Visualstudio的已经开发好了 可以在这里找到。 (如果低版本不适用,那么,我还要重新下载老版本vs开发一版) image.png image.png ,可这个就是不行,直接运行点这些按钮加载失败,而我直接不调试模式,则直接什么…

【运维类】智慧运维系统建设方案(PPT原件完整版)

建设方案目录: 1、智慧运维系统建设背景 2、智慧运维系统建设目标 3、智慧运维系统建设内容 4、智慧运维系统建设技术 5、智慧运维系统建设流程 6、智慧运维系统建设收益 企业对运维管理的需求: 1、提高运维效率:降低运维成本,提高…

我定制了一个属于自己的录屏软件

相信很多朋友都用过不同软件的录屏功能,但是这些软件多少还让存在一些缺点,让我们在录屏的时候不能得心应手。 今天我就来手把手教大家自己来制作一个私人订制的浏览器录屏器, 录屏器 我们可以使用浏览器的屏幕捕获API接口来帮助我们来捕获…

java里的序列化反序列化、HttpMessageConverter、Jackson、消息转化器、对象转化器...都是啥?

前段时间在学习SSM框架(spring boot、spring MVC、mybatis)后端项目的时候,发现他们的项目里:响应类Result类要实现Serializable接口、转化响应给前端的时间数据的格式要用到什么“消息转换器”MappingJackson2HttpMwssageConvert…

Vue:Vue3-TypeScript-Pinia-Vite-pnpm / 基础项目 / 20240807

一、项目技术栈 / 依赖 序号技术栈版本解释1node20.14.02vue 3.4.31 3vite 5.3.4 4TypeScript 5.2.2 5 types/node 22.0.2 解决TypeScript项目中缺少对应模块的类型定义文件的问题6 element-plus 2.7.8 ui组建7 types/js-cookie js-cookie 3.0.6 3.0.5 8 sass 1.77.8 9 hu…

zdppy+vue3+onlyoffice开发文档系统实战20240807上课笔记 解决了最近文档页面几个遗留的文档

小技巧 vite配置 open: true 可以自动打开浏览器。 目前 遗留任务 1、在名称前面,渲染这个文档的图标 2、大小的基本的单位是kb,超过1024kb则换成mb,主要是这两个单位 3、数据按照最近访问时间倒序 4、给文件名价格链接,实现和…

C语言深度剖析(部分)--剩下随缘更新

C语言深度剖析 关键字auto-最宽容大度的关键字 变量的分类 代码块:用{ }括起来的区域 局部变量:包含在代码块中的变量,局部变量具有临时性,进入代码块,自动形成局部变量,退出代码块自动释放。 全局变量…

鸿蒙AI功能开发【hiai引擎框架-人脸比对】 基础视觉服务

hiai引擎框架-人脸比对 介绍 本示例展示了使用hiai引擎框架提供的人脸比对能力。 本示例模拟了在应用里,选择两张图片,计算两个图中最大人脸的相似度 需要使用hiai引擎框架人脸比对接口hms.ai.face.faceComparator。 效果预览 使用说明:…

C++ STL专题 list的底层实现

目录 1.模拟实现list 2.节点模板讲解 3.迭代器模板讲解 3.1为什么template 有三个类型参数 (1).class T (2).class ref (3).class ptr 3.2 *重载 3.3 ->重载 3.4 前置和后置的重载 3.5 前置--和--后置的重载 3.6 和!的重载 4. list模板讲解 4.1 begin()函数 …

[译] How things get done on the Go Team

6天前,掌舵Go语言团队12年Rsc在golang-dev/群组发文宣布,将在9月1号后辞去当前职位,转去做 Gaby 和 Oscar. 这对于Go语言发展无疑是里程碑式的事件。 本篇内容是根据6月份他和另外两位同事参与Go Time音频录制内容的整理与翻译,英…

代码随想录算法训练营Day22 | Leetcode 77 组合 Leetcode 216 组合总和Ⅲ Leetcode17 电话号码的字母组合

前言 回溯算法中递归的逻辑不重要,只要掌握回溯的模板以及将问题转化为树形图,整个问题就很好解决了,比二叉树简单。 Leetcode 77 组合 题目链接:77. 组合 - 力扣(LeetCode) 代码随想录题解:…

K-means聚类算法的应用以及实现

K-means 聚类算法属于无监督学习,它会将相似的对象归到同一个簇中,该算法原理简单,执行效率高,并且容易实现,是解决聚类问题的经典算法。 尽管如此,任何一款算法都不可能做到完美无瑕,K-measn 算…

计算机毕业设计选题推荐-房屋租赁系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…