如何从/proc/pid/stat获取应用程序的总CPU使用率?

74

我想知道如何计算进程的总CPU使用率。

如果我执行命令 cat /proc/pid/stat,我认为相关字段是(来自lindevdoc.org):

  1. 在Jiffies中测量的用户代码CPU时间
  2. 在Jiffies中测量的内核代码CPU时间
  3. 包括子级的用户代码CPU时间
  4. 包括子级的内核代码CPU时间

所以,总时间是字段14到17的总和吗?

6个回答

179

准备工作

要计算特定进程的CPU使用率,您需要以下内容:

  1. /proc/uptime
    • #1 系统的运行时间(秒)
  2. /proc/[PID]/stat
    • #14 utime - 用户代码中使用的CPU时间,以时钟滴答为单位
    • #15 stime - 内核代码中使用的CPU时间,以时钟滴答为单位
    • #16 cutime - 等待子进程在用户代码中使用的CPU时间(以时钟滴答为单位)
    • #17 cstime - 等待子进程在内核代码中使用的CPU时间(以时钟滴答为单位)
    • #22 starttime - 进程启动时的时间,以时钟滴答为单位
  3. 您的系统的赫兹数(每秒钟的时钟滴答数)。

计算方法

首先我们确定了进程的总时间:

total_time = utime + stime

我们还需要决定是否要包括子进程的时间。如果要包括,那么我们将这些值添加到 total_time 中:

total_time = total_time + cutime + cstime

接下来我们获取自进程启动以来经过的总时间,以为单位:

seconds = uptime - (starttime / Hertz)

最后,我们计算CPU使用率百分比:

cpu_usage = 100 * ((total_time / Hertz) / seconds)

参见

Top和ps未显示相同的CPU结果

如何在Linux(C ++)中获取总CPU使用率

计算Linux进程的CPU使用率


10
你好,这将给出自应用程序启动以来的平均 CPU 使用率。如果一个进程在最近的5秒中占用了大部分 CPU 而在1小时内处于空闲状态,那么该代码仍将给出自其启动以来的平均值。 - user435739
1
我从同一个链接中获取了它,谢谢 :). 我已经发布了一个回答来回应我的问题,请检查一下。 - user435739
@Manish 很抱歉,我刚意识到我给你的链接得出的是内核HZ值,但在这种特殊情况下,你实际上需要的是USER_HZ值。 - Vilhelm Gray
@Volodymyr,你的awk命令是正确的:在这种情况下/proc/self/statutime参数是0。我怀疑你的困惑来自于/proc/self/stat的行为,它是指当前运行的进程的链接——即执行cat /proc/self/stat,但是它还不存在足够长的时间,使得utime大于0 - Vilhelm Gray
1
@T-D 我在方程式中使用的 uptime/proc/uptime 的第一个参数。我提到 /proc/uptime 的第二个参数是为了说明如何计算整个系统的总 CPU 使用率,而不是单个进程;由于我们只关心单个进程的 CPU 使用率,因此在方程式中未使用第二个参数。 - Vilhelm Gray
显示剩余14条评论

6

是的,你可以这么说。你可以使用以下公式将这些值转换为秒:

      sec = jiffies / HZ ; here - HZ = number of ticks per second

HZ值是可配置的 - 在内核配置时间完成。


6

这是一个我用BASH编写的简单解决方案。它是一个基于procfs的Linux/Unix系统监视器和进程管理器,类似于"top"或"ps"。有两个版本,一个是简单的单色版(快速),另一个是彩色版(略慢,但特别适合监控进程状态)。我按CPU使用率排序。

https://github.com/AraKhachatryan/top

  • 获取CPU使用率utimestimecutimecstimestarttime参数从/proc/[pid]/stat文件中获取。

  • stateppidprioritynicenum_threads参数也从/proc/[pid]/stat文件中获取。

  • 获取内存使用率residentdata_and_stack参数从/proc/[pid]/statm文件中获取。


    function my_ps
    {
        pid_array=`ls /proc | grep -E '^[0-9]+$'`
        clock_ticks=$(getconf CLK_TCK)
        total_memory=$( grep -Po '(?<=MemTotal:\s{8})(\d+)' /proc/meminfo )

        cat /dev/null > .data.ps

        for pid in $pid_array
        do
            if [ -r /proc/$pid/stat ]
            then
                stat_array=( `sed -E 's/(\([^\s)]+)\s([^)]+\))/\1_\2/g' /proc/$pid/stat` )
                uptime_array=( `cat /proc/uptime` )
                statm_array=( `cat /proc/$pid/statm` )
                comm=( `grep -Po '^[^\s\/]+' /proc/$pid/comm` )
                user_id=$( grep -Po '(?<=Uid:\s)(\d+)' /proc/$pid/status )

                user=$( id -nu $user_id )
                uptime=${uptime_array[0]}

                state=${stat_array[2]}
                ppid=${stat_array[3]}
                priority=${stat_array[17]}
                nice=${stat_array[18]}

                utime=${stat_array[13]}
                stime=${stat_array[14]}
                cutime=${stat_array[15]}
                cstime=${stat_array[16]}
                num_threads=${stat_array[19]}
                starttime=${stat_array[21]}

                total_time=$(( $utime + $stime ))
                #add $cstime - CPU time spent in user and kernel code ( can olso add $cutime - CPU time spent in user code )
                total_time=$(( $total_time + $cstime ))
                seconds=$( awk 'BEGIN {print ( '$uptime' - ('$starttime' / '$clock_ticks') )}' )
                cpu_usage=$( awk 'BEGIN {print ( 100 * (('$total_time' / '$clock_ticks') / '$seconds') )}' )

                resident=${statm_array[1]}
                data_and_stack=${statm_array[5]}
                memory_usage=$( awk 'BEGIN {print( (('$resident' + '$data_and_stack' ) * 100) / '$total_memory'  )}' )

                printf "%-6d %-6d %-10s %-4d %-5d %-4s %-4u %-7.2f %-7.2f %-18s\n" $pid $ppid $user $priority $nice $state $num_threads $memory_usage $cpu_usage $comm >> .data.ps

            fi
        done

        clear
        printf "\e[30;107m%-6s %-6s %-10s %-4s %-3s %-6s %-4s %-7s %-7s %-18s\e[0m\n" "PID" "PPID" "USER" "PR" "NI" "STATE" "THR" "%MEM" "%CPU" "COMMAND"
        sort -nr -k9 .data.ps | head -$1
        read_options
    }

screenshot of working script


2

这里是我获取应用程序CPU使用率的另一种方法。我在Android上完成了这个操作,它会进行内核顶部调用,并使用top返回的内容获取您的应用程序PID的CPU使用率。

public void myWonderfulApp()
{
   // Some wonderfully written code here
   Integer lMyProcessID = android.os.Process.myPid();
   int lMyCPUUsage = getAppCPUUsage( lMyProcessID );
   // More magic
}


// Alternate way that I switched to.  I found the first version was slower
// this version only returns a single line for the app, so far less parsing
// and processing.
public static float getTotalCPUUsage2()
{
    try
    {
        // read global stats file for total CPU
        BufferedReader reader = new BufferedReader(new FileReader("/proc/stat"));
        String[] sa = reader.readLine().split("[ ]+", 9);
        long work = Long.parseLong(sa[1]) + Long.parseLong(sa[2]) + Long.parseLong(sa[3]);
        long total = work + Long.parseLong(sa[4]) + Long.parseLong(sa[5]) + Long.parseLong(sa[6]) + Long.parseLong(sa[7]);
        reader.close();

        // calculate and convert to percentage
        return restrictPercentage(work * 100 / (float) total);
    }
    catch (Exception ex)
    {
        Logger.e(Constants.TAG, "Unable to get Total CPU usage");
    }

    // if there was an issue, just return 0
    return 0;
}

// This is an alternate way, but it takes the entire output of 
// top, so there is a fair bit of parsing.
public static int getAppCPUUsage( Integer aAppPID)
{
    int lReturn = 0;
    // make sure a valid pid was passed
    if ( null == aAppPID && aAppPID > 0)
    {
        return lReturn;
    }

    try
    {
        // Make a call to top so we have all the processes CPU
        Process lTopProcess = Runtime.getRuntime().exec("top");
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(lTopProcess.getInputStream()));

        String lLine;

        // While we have stuff to read and we have not found our PID, process the lines
        while ( (lLine = bufferedReader.readLine()) != null )
        {
            // Split on 4, the CPU % is the 3rd field .
            // NOTE: We trim because sometimes we had the first field in the split be a "".
            String[] lSplit = lLine.trim().split("[ ]+", 4);

            // Don't even bother if we don't have at least the 4
            if ( lSplit.length > 3 )
            {
                // Make sure we can handle if we can't parse the int
                try
                {
                    // On the line that is our process, field 0 is a PID
                    Integer lCurrentPID = Integer.parseInt(lSplit[0]);

                    // Did we find our process?
                    if (aAppPID.equals(lCurrentPID))
                    {
                        // This is us, strip off the % and return it
                        String lCPU = lSplit[2].replace("%", "");

                        lReturn = Integer.parseInt(lCPU);
                        break;
                    }
                }
                catch( NumberFormatException e )
                {
                    // No op.  We expect this when it's not a PID line
                }
            }
        }

        bufferedReader.close();
        lTopProcess.destroy();      // Cleanup the process, otherwise you make a nice hand warmer out of your device

    }
    catch( IOException ex )
    {
        // Log bad stuff happened
    }
    catch (Exception ex)
    {
        // Log bad stuff happened
    }

    // if there was an issue, just return 0
    return lReturn;
}

我想补充一下,这有时候并不是一个快速的操作。我曾经见过这个操作需要3秒以上的时间。最近我切换到了我提供的另一个答案。虽然不是非常快,但处理的行数更少。你只会得到你请求的PID的单行返回结果。 - Brian S
1
这是完全可以预料的,因为默认的最长延迟时间是3秒,你可以使用“-d 0”来避免它并立即获得结果。 - Kikiwa
getTotalCPUUsage2 获取自开机以来的平均 CPU 使用率,因为 /proc/stat 返回自设备启动以来聚合的值。 - William Reed
为了计算瞬时CPU使用率,需要执行两次操作,计算出/proc/stat值的差异,并从该差异中计算出CPU使用率。 - William Reed

2
如果需要计算最近10秒钟一个进程使用了多少CPU%,可以按照以下步骤进行:
1. 获取进程的总时间(13+14)以jiffies为单位,记为t1;获取进程的启动时间(22)以jiffies为单位,记为s1。 2. 等待10秒钟后,再次获取进程的总时间和启动时间,分别记为t2和s2。 3. 计算CPU%的公式为:(t2-t1)*100/(s2-s1)。
请注意,以上内容中的jiffies是Linux内核中的一个时间单位,用于表示CPU时间。

要获取在经过的一段时间内(例如10秒)的CPU使用率,您需要将经过的进程时间除以总经过时间。 - Vilhelm Gray
s2-s1不会等于10秒钟吗? - user435739
@VilhelmGray,你觉得怎么样? - user435739
我可能误解了你的方程,但如果你使用 starttime(22),那么 s2 和 s1 将是相同的。 starttime 是进程启动时刻,因此该值在进程生命周期中保持不变。 - Vilhelm Gray
哦,糟糕。我混淆了,因为我以为它是自启动以来的时间。 - user435739
现在问题是如何获取用户HZ。 - user435739

-4

这就是你要找的内容:

//USER_HZ detection, from openssl code
#ifndef HZ
# if defined(_SC_CLK_TCK) \
     && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000)
#  define HZ ((double)sysconf(_SC_CLK_TCK))
# else
#  ifndef CLK_TCK
#   ifndef _BSD_CLK_TCK_ /* FreeBSD hack */
#    define HZ  100.0
#   else /* _BSD_CLK_TCK_ */
#    define HZ ((double)_BSD_CLK_TCK_)
#   endif
#  else /* CLK_TCK */
#   define HZ ((double)CLK_TCK)
#  endif
# endif
#endif

这段代码实际上来自于 cpulimit,但使用了 OpenSSL 的片段。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接