atop分析小记
- atop这个工具相当NB
- 项目中需要用到它的磁盘使用率统计值,为了一探究竟,挖了下它的代码
atopsar
atopsar实际就是atop的一个链接指向。
从atop.c的main源码可以看到
int
main(int argc, char *argv[])
{
char *p;
if ((p = strrchr(argv[0], '/')))
p++;
else
p = argv[0];
if (memcmp(p, "atopsar", 7) == 0)
return atopsar(argc, argv);
}
源码编译
实际读起代码来,还比较繁琐,为了快速找到我关注的东西,考虑干脆自己编译一份可执行文件,然后通过gdb单步跟踪,一步到位。
可惜实际结果并不是很理想,还是坑了好一会儿……
源码编译步骤
虽然README介绍的相当干练:
For interactive use, it is sufficient to install ATOP with the command
(as root):
make systemdinstall
or
make sysvinstall
可惜我make直接报错(大意如此):
no rule to make version.h needed by version.c ...
发现需要通过mkversion
脚本生成 version.h
,
实际,项目需要的 version.c
也木有。
附上我的
// version.h
#define ATOPVERS "ATOP3"
#define ATOPDATE "2017/07/18 22:24:58"
//version.c
char *getstrvers(void) {
return "Version 3.2.0";
}
unsigned short getnumvers(void) {
return (0x1313);
}
第二件坑爹的事情
虽然README提到这句,可是,它完全没提是必须的啊。
我想偷个懒,直接注释相关引用,却发现进了另一个坑。
最后老实下载了这个包,注意官网提供的netatop
对内核有要求,
我为了匹配内核版本号,下载了netatop-0.3.tar.gz
The kernel module 'netatop' can be downloaded and installed separately from www.atoptool.nl/downloadnetatop.php
第三件事
netatop-0.3.tar.gz
下载好了,解压缩后,一定要重命名到Netatop
.
主要原因是: atop
源码里面的两个依赖文件是做了链接的。。。
lrwxrwxrwx 1 ubuntu ubuntu 21 Mar 24 15:54 netatopd.h -> ../Netatop/netatopd.h
lrwxrwxrwx 1 ubuntu ubuntu 20 Mar 24 15:54 netatop.h -> ../Netatop/netatop.h
编译
修改 Makefile
CFLAGS += -O2 -I.
# 改成
CFLAGS += -O0 -g -I.
编译 make
源码小小分析
一路从 main
函数追踪的话,大概是这么一条路
main() -> atopsar() -> engine()
-> reportheader()
-> photosyst()
主要的逻辑在 photosyst
这个函数里。
里面罗列了读取系统的各种信息。
CPU,内存,磁盘,网络、、、
我关注的地方
// 这里是实际读取磁盘信息的部分
// 实际是读取了 /proc/diskstats文件
/*
** check if disk-statistics are provided (kernel 2.6 onwards)
*/
if ( (fp = fopen("diskstats", "r")) != NULL)
{
char diskname[256];
struct perdsk tmpdsk;
si->dsk.ndsk = 0;
si->dsk.nmdd = 0;
si->dsk.nlvm = 0;
while ( fgets(linebuf, sizeof(linebuf), fp) )
{
nr = sscanf(linebuf,
"%d %d %255s %lld %*d %lld %*d "
"%lld %*d %lld %*d %*d %lld %lld",
&major, &minor, diskname,
&tmpdsk.nread, &tmpdsk.nrsect,
&tmpdsk.nwrite, &tmpdsk.nwsect,
&tmpdsk.io_ms, &tmpdsk.avque );
/*
** check if this line concerns the entire disk
** or just one of the partitions of a disk (to be
** skipped)
*/
if (nr == 9) /* full stats-line ? */
{
switch ( isdisk(major, minor, diskname,
&tmpdsk, MAXDKNAM) )
{
case NONTYPE:
continue;
case DSKTYPE:
if (si->dsk.ndsk < MAXDSK-1)
si->dsk.dsk[si->dsk.ndsk++] = tmpdsk;
break;
case MDDTYPE:
if (si->dsk.nmdd < MAXMDD-1)
si->dsk.mdd[si->dsk.nmdd++] = tmpdsk;
break;
case LVMTYPE:
if (si->dsk.nlvm < MAXLVM-1)
si->dsk.lvm[si->dsk.nlvm++] = tmpdsk;
break;
}
}
}
/*
** set terminator for table
*/
si->dsk.dsk[si->dsk.ndsk].name[0] = ' ';
si->dsk.mdd[si->dsk.nmdd].name[0] = ' ';
si->dsk.lvm[si->dsk.nlvm].name[0] = ' ';
fclose(fp);
}
static struct {
char *regexp;
regex_t compreg;
void (*modname)(unsigned int, unsigned int,
char *, struct perdsk *, int);
int retval;
} validdisk[] = {
{ "^ram[0-9][0-9]*$", {0}, (void *)0, NONTYPE, },
{ "^loop[0-9][0-9]*$", {0}, (void *)0, NONTYPE, },
{ "^sd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
{ "^dm-[0-9][0-9]*$", {0}, lvmmapname, LVMTYPE, },
{ "^md[0-9][0-9]*$", {0}, nullmodname, MDDTYPE, },
{ "^vd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
{ "^hd[a-z]$", {0}, nullmodname, DSKTYPE, },
{ "^rd/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
{ "^cciss/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
{ "^fio[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
{ "/host.*/bus.*/target.*/lun.*/disc", {0}, abbrevname1, DSKTYPE, },
{ "^xvd[a-z][a-z]*[0-9]*$", {0}, nullmodname, DSKTYPE, },
{ "^dasd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
{ "^mmcblk[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
{ "^emcpower[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
};
// isdisk 通过预设的正则表达式过滤规则
// 判断 /proc/diskstats 里的信息到底是不是磁盘
// 正则在上文列出
static int
isdisk(unsigned int major, unsigned int minor,
char *curname, struct perdsk *px, int maxlen)
{
static int firstcall = 1;
register int i;
if (firstcall) /* compile the regular expressions */
{
for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++)
regcomp(&validdisk[i].compreg, validdisk[i].regexp,
REG_NOSUB);
firstcall = 0;
}
/*
** try to recognize one of the compiled regular expressions
*/
for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++)
{
if (regexec(&validdisk[i].compreg, curname, 0, NULL, 0) == 0)
{
/*
** name-string recognized; modify name-string
*/
if (validdisk[i].retval != NONTYPE)
(*validdisk[i].modname)(major, minor,
curname, px, maxlen);
return validdisk[i].retval;
}
}
return NONTYPE;
}