ubuntu下top命令实现分析

在分析top命令代码实现前,先啰嗦几句。

最近的一个linux项目要获取cpu和内存使用率。由于linux并未提供相关的api,因此,只能通过其他手段来获取;

最开始使用的方式可简单描述如下:

1.构造一个命令字符串,该字符串的功能是通过top命令获取cpu的空闲率;

2.使用popen执行该命令;

3.使用getline等io函数从管道中读取cpu的空闲率;

4.将字符串的空闲率转换成整形值,cpu usage = 100 - idle;

至此,通过上述简单粗暴的办法完成了cpu的使用率计算。

在开始时,这个程序跑在openSuse系统上,并未发现什么问题,直到最近,把该程序放到red hat上跑时,cpu使用率计算异常,为100%。

把相关代码从项目中抠出来,增加日志打印,发现问题出在第3步,从管道读取cpu的空闲率。不同的系统上,top命令输出的数据格式存在细微差异。而上述获取cpu空闲率的方法刚好依赖于数据输出格式,所以才会出现换一种操作系统,cpu使用率计算错误问题。

为了解决上述问题,查看了ubuntu的top命令实现源码,其cpu使用率的计算方式如下:

1.打开“/proc/stat”文件。

2.读取该文件的首行,该行记录了从系统启动以来,cpu的使用信息:

cpu  1660 6942 2790 1518455 952 720 0 0 0 0
计算中我们使用到的有:
user:1660       用户态的运行时间
nice:6942       nice值为负的进程所占用的CPU时间
system:2790     核心态的运行时间
idle:1518455    除IO等待时间以外的其它等待时间
iowait:952      IO等待时间
irq:720         硬中断时间
softirq:0       软中断时间
stealstolen:0   被hypervisor偷去的时间

3.使用sscanf解析读取到的信息;

4.使用解析后的数据进行计算,cpu空闲率的计算公式如下:

totalframe = user + nice + system + idle + iowait + irq + softirq + stealstolen;
idlerate = (idleframe_new - idleframe_old) * 100.0 / (totalframe_new - totalframe_old);
xxx_old表示上次计算得出的值, xxx_new表示本次获取的值。

通过以上4步,就可以计算出从上一次计算到本次计算这段时间内的cpu的空闲率。

以下是从top命令的实现代码中获取到的关键代码:

int main ( int dont_care_argc, char *argv[])
{
......
for (;;) {
......
frame_make(); //刷新cpu使用率等信息;
......
select( 0, NULL, NULL, NULL, &tv); //定时,默认超时时间为3s;
......
}
}

static void frame_make ( void)
{
......
summary_show(); //显示概要信息;
......
}

static proc_t ** summary_show ( void)
{
......
smpcpu = cpus_refresh(smpcpu); //获取cpu信息;
......
summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):"); //输出cpu信息;
......
}

static CPU_t * cpus_refresh ( CPU_t *cpus)
{
......
/* by opening this file once, we'll avoid the hit on minor page faults
(sorry Linux, but you'll have to close it for us) */
if (!fp) {
fp = fopen( "/proc/stat", "r");
...
cpus = alloc_c(( 1 + Cpu_tot) * sizeof( CPU_t));
}
rewind(fp);
fflush(fp);

// first value the last slot with the cpu summary line
if (! fgets(buf, sizeof(buf), fp)) std_err( "failed /proc/stat read");
cpus[Cpu_tot]. x = 0; // FIXME: can't tell by kernel version number
cpus[Cpu_tot]. y = 0; // FIXME: can't tell by kernel version number
cpus[Cpu_tot]. z = 0; // FIXME: can't tell by kernel version number
num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
&cpus[Cpu_tot]. u,
&cpus[Cpu_tot]. n,
&cpus[Cpu_tot]. s,
&cpus[Cpu_tot]. i,
&cpus[Cpu_tot]. w,
&cpus[Cpu_tot]. x,
&cpus[Cpu_tot]. y,
&cpus[Cpu_tot]. z
);
......
return cpus;
}

static void summaryhlp ( CPU_t *cpu, const char *pfx)
{
......
u_frme = cpu-> u - cpu-> u_sav;
s_frme = cpu-> s - cpu-> s_sav;
n_frme = cpu-> n - cpu-> n_sav;
i_frme = TRIMz(cpu-> i - cpu-> i_sav);
w_frme = cpu-> w - cpu-> w_sav;
x_frme = cpu-> x - cpu-> x_sav;
y_frme = cpu-> y - cpu-> y_sav;
z_frme = cpu-> z - cpu-> z_sav;
tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
if (tot_frme < 1) tot_frme = 1;
scale = 100.0 / ( float)tot_frme;

// display some kinda' cpu state percentages
// (who or what is explained by the passed prefix)
show_special(
0,
fmtmk(
States_fmts,
pfx,
( float)u_frme * scale,
( float)s_frme * scale,
( float)n_frme * scale,
( float)i_frme * scale,
( float)w_frme * scale,
( float)x_frme * scale,
( float)y_frme * scale,
( float)z_frme * scale
)
);
......

// remember for next time around
cpu-> u_sav = cpu-> u;
cpu-> s_sav = cpu-> s;
cpu-> n_sav = cpu-> n;
cpu-> i_sav = cpu-> i;
cpu-> w_sav = cpu-> w;
cpu-> x_sav = cpu-> x;
cpu-> y_sav = cpu-> y;
cpu-> z_sav = cpu-> z;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值