2

我有一个以百分比计算 CPU 使用率的 shell 脚本。因为我想扩展功能,并且想在 Ruby 中执行此操作,而不是从 Ruby 调用 shell 脚本。

我尝试用 Ruby 重写代码,但最终输出存在差异。

shell 代码的输出在 5% 到 10% 之间,而 Ruby 代码的输出在 97.5% 到 97.8% 之间。

这是红宝石代码:

result = `cat /proc/stat | grep '^cpu '`.split(" ")
result.delete("cpu")
idle_time0 = result[4].to_i
total_time0 = 0

result.each do |partial_time|
  total_time0 += partial_time.to_i
end


sleep 0.5


result = `cat /proc/stat | grep '^cpu '`.split(" ")
result.delete("cpu")
idle_time = result[4].to_i
total_time = 0

result.each do |partial_time|
  total_time += partial_time.to_i
end

diff_idle = idle_time - idle_time0
diff_total = total_time - total_time0

diff_usage = (1000*(diff_total - diff_idle)/(diff_total+5).to_f)/10.0

p diff_usage

这是外壳脚本:

#!/bin/bash

CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics.
unset CPU[0]                          # Discard the "cpu" prefix.
IDLE=${CPU[4]}                        # Get the idle CPU time.

# Calculate the total CPU time.
TOTAL=0
for VALUE in "${CPU[@]}"; do
  let "TOTAL=$TOTAL+$VALUE"
done

# Remember the total and idle CPU times for the next check.
PREV_TOTAL="$TOTAL"
PREV_IDLE="$IDLE"

# Wait before checking again.
sleep 0.5

CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics.
unset CPU[0]                          # Discard the "cpu" prefix.
IDLE=${CPU[4]}                        # Get the idle CPU time.

# Calculate the total CPU time.
TOTAL=0
for VALUE in "${CPU[@]}"; do
  let "TOTAL=$TOTAL+$VALUE"
done

# Calculate the CPU usage since we last checked.
let "DIFF_IDLE=$IDLE-$PREV_IDLE"
let "DIFF_TOTAL=$TOTAL-$PREV_TOTAL"
let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10"

echo -en "\rCPU: $DIFF_USAGE%  \b\b"
4

2 回答 2

1

ProGNOMmers 已经指出了您的索引错误,但我想指出,像您所做的那样翻译成 ruby​​ 几乎没有任何好处。您的 ruby​​ 代码读起来和 bash 一样难看,但您仍然要花两次钱来获取/proc/stat. 通过使用 ruby​​ 作为高级语言,您可以使其更具可读性、更不容易出错且更高效。

这是一个示例重写。我做了一个小方法,将 /proc/stat 行转换为具有有意义名称的结构,因此不会出现更多的数组索引问题,并且始终清楚您引用的是哪个计时器值。我已经使用File::readlinesEnumerable#grep读取了 proc 文件系统,而无需掏空。我使用 printf 格式来获得您似乎正在寻找的百分位舍入效果。

#!/usr/bin/env ruby

# http://man7.org/linux/man-pages/man5/proc.5.html
CpuTimes = Struct.new :user, :nice, :system, :idle, :iowait, :irq,
                      :softirq, :steal, :guest, :guest_nice, :total

def get_cpu_times
  parts = File.readlines('/proc/stat').grep(/^cpu /).first.split
  times = parts[1..-1].map(&:to_i)
  CpuTimes[ *times ].tap { |r| r[:total] = times.reduce(:+) }
end

c0 = get_cpu_times
sleep 0.5
c1 = get_cpu_times

idle  = c1.idle - c0.idle
total = c1.total - c0.total
usage = total - idle
printf "CPU: %.1f%%", 100.0 * usage / total
于 2013-04-11T17:11:35.110 回答
1

问题是在 bash 中取消设置数组值会产生奇怪的效果:

CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics.

let COUNT=0
while [ $COUNT -lt "${#CPU[@]}" ]; do
  echo "value at $COUNT: ${CPU[$COUNT]}"
  let "COUNT=$COUNT+1"
done

unset CPU[0]                          # Discard the "cpu" prefix.

let COUNT=0
while [ $COUNT -lt "${#CPU[@]}" ]; do
  echo "value at $COUNT: ${CPU[$COUNT]}"
  let "COUNT=$COUNT+1"
done

这输出:

value at 0: cpu
value at 1: 763993
value at 2: 116443
value at 3: 179513
value at 4: 22344343
value at 5: 536446
value at 6: 5
value at 7: 640
value at 8: 0
value at 9: 0
value at 10: 0
value at 0:          # This should be 763993
value at 1: 763993   # and so on...
value at 2: 116443
value at 3: 179513
value at 4: 22344343
value at 5: 536446
value at 6: 5
value at 7: 640
value at 8: 0
value at 9: 0        # ...and the last 0 value is vanished!

无论如何,解决方案是减少 ruby​​ idle_time 索引:

...
idle_time0 = result[3].to_i
...
idle_time = result[3].to_i
...
于 2013-04-11T16:16:09.127 回答