赞
踩
在Linux中,cpustat定期转储正在运行的进程的当前CPU利用率统计信息。cpustat已被优化为具有最小的CPU开销,与top相比,通常使用约35%的CPU。cpustat还包括一些简单的统计分析选项,可以帮助描述CPU的加载方式。
cpustat介绍
cpustat是一个转储当前运行任务(即进程或内核线程)的CPU利用率的程序。cpustat用于监视系统中长期存在的进程的活动,例如守护进程、内核线程以及典型的用户进程。
cpustat [ options ] [delay [count]]
cpustat命令行选项:
-h帮助 -a 基于所有CPU节拍而不是一个CPU来计算CPU利用率 -c 从进程命令字段获取命令名(CPU成本较低) -d 删除目录basename命令信息 -D 显示运行结束时CPU利用率统计数据的分布 -g 显示运行结束时CPU利用率统计的总计 -i 忽略了统计数据中的cpustat -l 显示长(完整)命令信息 -n 指定要显示的任务数 -q 安静运行,使用选项-r非常有用 -r 指定要将样本转储到的逗号分隔值输出文件。 -s 显示简短命令信息 -S 时间戳输出 -t 指定忽略小于此值的样本的任务刻度计数阈值。 -T 显示总CPU利用率统计数据 -x 显示额外的统计数据(平均负载、平均cpu频率等)
对于在采样时间内消耗了一些CPU的每个正在运行的任务,将显示以下信息:
标题说明
%CPU 使用的CPU总数(百分比)
%USR 空间中使用的USR CPU(百分比)
%SYS (内核)空间中使用的SYS CPU(百分比)
PID 进程ID
S进程状态,R(运行),S(休眠),D(等待,磁盘休眠),T(停止),T(跟踪停止),W(寻呼),X(死),X(死)、K(唤醒),W),P(停止)。
CPU采样时进程使用的CPU。
Time 自进程启动以来使用的CPU总时间。
Task 进程命令行信息(来自进程命令行或命令字段)
cpustat定期报告正在运行的任务的当前CPU利用率,并且可以在运行结束时报告每个CPU和每个任务的利用率统计信息。
cpustat工具实现的一些知识点
/proc/sys/kernel/pid_max #查系统支持的最大线程数,一般会很大,相当于理论值。
该文件保存了进程的完整命令行. 如果该进程已经 被交换出内存, 或者该进程已经僵死, 那么就没有 任何东西在该文件里, 这时候对该文件的读操作将返回零 个字符. 该文件以空字符null 而不是换行符作为结 束标志.
stat 进程状态信息, 被命令 ps(1) 使用. 现将该文件里各域, 以及他们的 scanf(3) 格式说明符, 按顺序分述如下: pid %d 进程标识. comm %s 可执行文件的文件名, 包括路径. 该文件是否可 见取决于该文件是否已被交换出内存. state %c ";RSDZT"; 中的一个, R 是正在运行, S 是 在可中断的就绪态中睡眠, D 是在不可中 断的等待或交换态中睡眠, Z 是僵死, T 是被跟踪或被停止(由于收到信号). ppid %d 父进程 PID. pgrp %d 进程的进程组 ID. session %d 进程的会话 ID. tty %d 进程所使用终端. tpgid %d 当前拥有该进程所连接终端的进程所在的进程 组 ID. flags %u 进程标志. 目前每个标志都设了数学位, 所以输出里就不包括该位. crt0.s 检查数学仿真 这可能是一个臭虫, 因为不是每个进 程都是用 c 编译的程序. 数学位应该是十 进制的 4, 而跟踪位应该是十进制的 10. minflt %u 进程所导致的小错误(minor faults)数目, 这样的 小错误(minor faults)不需要从磁盘重新载入一个 内存页. cminflt %u 进程及其子进程所导致的小错误(minor faults)数目. majflt %u 进程所导致的大错误(major faults)数目, 这样的 大错误(major faults)需要重新载入内存页. cmajflt %u 进程及其子进程所导致的大错误(major faults)数目. utime %d 进程被调度进用户态的时间(以 jiffy 为单 位, 1 jiffy=1/100 秒,另外不同硬件体系略有不同). stime %d 进程被调度进内核态的时间, 以 jiffy 为 单位. cutime %d 进程及其子进程被调度进用户态的时间, 以 jiffy 为单位. cstime %d 进程及其子进程被调度进内核态的时间, 以 jiffy 为单位. counter %d 如果进程不是当前正在运行的进程, 就是 进程在下个时间片当前可以拥有的最大时 间, 以 jiffy 为单位. 如果进程是当前正 在运行的进程, 就是当前时间片中所剩下 jiffy 数目. priority %d 标准优先数只再加上 15, 在内核里该值总 是正的. timeout %u 当前至进程的下一次间歇时间, 以 jiffy 为单位. itrealvalue %u 由于计时间隔导致的下一个 SIGALRM 发送进程的时延,以 jiffy 为单位. starttime %d 进程自系统启动以来的开始时间, 以 jiffy 为单位. vsize %u 虚拟内存大小. rss %u Resident Set Size(驻留大小): 进程所占用的真实内 存大小, 以页为单位, 为便于管理而减去 了 3. rss 只包括正文, 数据以及堆栈的空间, 但不包括尚未要求装入内存的或已被交换出去的. rlim %u 当前进程的 rss 限制, 以字节为单位, 通 常为 2,147,483,647. startcode %u 正文部分地址下限. endcode %u 正文部分地址上限. startstack %u 堆栈开始地址. kstkesp %u esp(32 位堆栈指针) 的当前值, 与在进程 的内核堆栈页得到的一致. kstkeip %u EIP(32 位指令指针)的当前值. signal %d 待处理信号的 bitmap(通常为 0). blocked %d 被阻塞信号的 bitmap(对 shell 通常是 0, 2). sigignore %d 被忽略信号的 bitmap. sigcatch %d 被俘获信号的 bitmap. wchan %u 进程在其中等待的通道, 实际是一个系统 调用的地址. 如果你需要文本格式的, 也 可以在名字列表中找到. (如果有最新版本的 /etc/psdatabase, 你 可以在 ps -l 的结果中的 WCHAN 域看到)
查看CPU核心的当前运行频率,使用如下的命令
其中cpu0指第一个核心,根据情况可以换成cpu1, cpu2,cpu3…,在online文件里描述cpu的状态,0代表下线,1代表上线
前三个数字是1、5、15分钟内的平均进程数(有人认为是系统负荷的百分比,其实不然,有些时候可以看到200甚至更多)。
第四个值的分子是正在运行的进程数,分母是进程总数,最后一个是最近运行的进程ID号。
这里的平均负载也就是可运行的进程的平均数。
static void cpustat_top_setup(void)
{
(void)initscr(); //屏幕就会初始化并进入curses 模式
(void)cbreak(); //cbreak()函数都可以禁止行缓冲
(void)noecho(); // 禁止输入的字符出现在屏幕上
(void)nodelay(stdscr, 1); // nodelay选项使getch成为非阻塞调用。如果没有输入就绪,getch将返回ERR。
(void)keypad(stdscr, 1); //允许使用功能键,例如:F1、F2、方向键等功能键。
(void)curs_set(0);// curs_set例程将光标状态设置为不可见、正常或非常可见,可见性分别为0、1或2。
}
Linux下C/C++实现cpustat
... static int get_pid_max_digits(void) { ssize_t n; int digits, fd; const int default_digits = 6; const int min_digits = 5; char buf[32]; digits = default_digits; fd = open("/proc/sys/kernel/pid_max", O_RDONLY); if (fd < 0) goto ret; n = read(fd, buf, sizeof(buf) - 1); (void)close(fd); if (n < 0) goto ret; buf[n] = '\0'; digits = 0; while (buf[digits] >= '0' && buf[digits] <= '9') digits++; if (digits < min_digits) digits = min_digits; ret: return digits; } static void info_banner_dump(const double time_now,int umax_w,int smax_w,int usmax_w) { static char str[256]; char *ptr = str; int i; char tmp[256]; size_t n, sz = sizeof(str); snprintf(tmp, sizeof(tmp), "%*s %*s %*s ", usmax_w, "%CPU", umax_w, "%USR", smax_w, "%SYS"); n = strlen(tmp); (void)strncpy(ptr, tmp, sizeof(str)); ptr += n; sz -= n; for (i = 0; i < pid_max_digits - 3; i++, ptr++) *ptr = ' '; sz -= i; (void)strncpy(ptr, "PID S CPU Time Task", sz); ptr += 23; if (UNLIKELY(opt_flags & OPT_TIMESTAMP)) { struct tm tm; get_tm(time_now, &tm); (void)strncpy(ptr, " (", 4); ptr += 3; ptr += putint(ptr, 2, tm.tm_hour, true); *ptr = ':'; ptr++; ptr += putint(ptr, 2, tm.tm_min, true); *ptr = ':'; ptr++; ptr += putint(ptr, 2, tm.tm_sec, true); *ptr = ')'; ptr++; } *ptr = '\0'; df.df_putstrnl(str, ptr - str); } ... static inline void info_total_dump(const double u_total,const double s_total) { if (UNLIKELY(opt_flags & OPT_TOTAL)) { char buffer[256], *ptr = buffer; ptr += putdouble(ptr, u_total + s_total, 100, 6); *(ptr++) = ' '; ptr += putdouble(ptr, u_total, 100, 6); *(ptr++) = ' '; ptr += putdouble(ptr, s_total, 100, 6); *(ptr++) = ' '; ptr += putstr(ptr, 5, "Total"); df.df_putstrnl(buffer, ptr - buffer); } } static char *load_average(void) { static char buffer[4096]; char *ptr = buffer; ssize_t len; int fd, skip = 3; if (UNLIKELY((fd = open("/proc/loadavg", O_RDONLY)) < 0)) goto unknown; len = read(fd, buffer, sizeof(buffer) - 1); (void)close(fd); if (UNLIKELY(len < 1)) goto unknown; buffer[len] = '\0'; for (;;) { if (*ptr == '\0') { skip--; break; } if (*ptr == ' ') { skip--; if (skip == 0) { *ptr = '\0'; break; } } ptr++; } if (skip != 0) goto unknown; return buffer; unknown: return "unknown"; } ... int main(int argc, char **argv) { ... for (;;) { int c = getopt(argc, argv, "acdDghiln:qr:sSt:Tp:xX"); if (c == -1) break; switch (c) { case 'a': opt_flags |= OPT_TICKS_ALL; break; case 'c': opt_flags |= OPT_CMD_COMM; break; case 'd': opt_flags |= OPT_DIRNAME_STRIP; break; case 'D': opt_flags |= (OPT_SAMPLES | OPT_DISTRIBUTION); break; case 'g': opt_flags |= OPT_GRAND_TOTAL; break; case 'h': show_usage(); exit(EXIT_SUCCESS); case 'i': opt_flags |= OPT_IGNORE_SELF; break; case 'l': opt_flags |= OPT_CMD_LONG; break; case 'n': errno = 0; n_lines = (int32_t)strtol(optarg, NULL, 10); if (errno) { (void)fprintf(stderr, "Invalid value for -n option\n"); exit(EXIT_FAILURE); } if (n_lines < 1) { (void)fprintf(stderr, "-n option must be greater than 0\n"); exit(EXIT_FAILURE); } break; case 'p': errno = 0; opt_pid = strtol(optarg, NULL, 10); if (errno) { (void)fprintf(stderr, "Invalid value for -o option\n"); exit(EXIT_FAILURE); } opt_flags |= OPT_MATCH_PID; break; case 's': opt_flags |= OPT_CMD_SHORT; break; case 'S': opt_flags |= OPT_TIMESTAMP; break; case 't': opt_threshold = atof(optarg); if (opt_threshold < 0.0) { (void)fprintf(stderr, "-t threshold must be 0 or more.\n"); exit(EXIT_FAILURE); } break; case 'T': opt_flags |= OPT_TOTAL; break; case 'q': opt_flags |= OPT_QUIET; break; case 'r': csv_results = optarg; opt_flags |= OPT_SAMPLES; break; case 'x': opt_flags |= OPT_EXTRA_STATS; break; case 'X': opt_flags |= OPT_TOP; break; default: show_usage(); exit(EXIT_FAILURE); } } ... }
运行结果:
当不带任何参数运行时,cpustat 默认会显示以下信息:
cpustat
每秒转储CPU统计数据,直到停止。
cpustat -n 20 60
每60秒转储前20个CPU消耗任务,直到停止。
cpustat 10 5
每10秒转储一次CPU统计数据,仅5次。
cpustat -x -D -a 1 300
每隔5分钟收集一次统计数据,并显示额外的CPU统计数据运行结束时每个任务和每个CPU的利用率分布。此外,比例CPU利用率除以CPU数量,因此100%利用率意味着100%CPU而不是1个CPU的100%。
cpustat -s 100 -s 10 -n 20
这将每100毫秒对所有进程进行一次采样,并在10次采样后(即每5秒)汇总此数据。
cpustat 5 5 -gxDST
If you need the complete source code of cpustat, please add WeChat number (c17865354792)
总结
cpustat 是 Linux 下一个用于linux下的CPU使用率监控工具。
Welcome to follow WeChat official account【程序猿编码】
参考:man 5 proc
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。