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;
}
---------------------
作者:鸟哥的鸟叔
来源:CSDN
原文:https://blog.csdn.net/ubuntu2016/article/details/79439658
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/Zhanganliu/article/details/86593266
今日推荐