topコマンドで得られるCPU使用率
やりたいことは、 top(1) の %CPU の求め方を知りたいので、ソースを追いかける。
環境は Amazon Linux 2。
まずは、ソースファイルを探す所からなので、rpm -f を使って topコマンドをインストールしたパッケージ情報を参照すると、 https://sourceforge.net/projects/procps-ng/ であることが分かった。
$ rpm -qfi `which top`
Name : procps-ng
Version : 3.3.10
Release : 26.amzn2
Architecture: x86_64
Install Date: Thu 28 Apr 2022 07:54:28 PM UTC
Group : Applications/System
Size : 758091
License : GPL+ and GPLv2 and GPLv2+ and GPLv3+ and LGPLv2+
Signature : RSA/SHA256, Tue 22 Oct 2019 08:43:34 PM UTC, Key ID 11cf1f95c87f5b1a
Source RPM : procps-ng-3.3.10-26.amzn2.src.rpm
Build Date : Wed 14 Aug 2019 12:01:21 AM UTC
Build Host : build.amazon.com
Relocations : (not relocatable)
Packager : Amazon Linux
Vendor : Amazon Linux
URL : https://sourceforge.net/projects/procps-ng/
Summary : System and process monitoring utilities
Description :
The procps package contains a set of system utilities that provide
system information. Procps includes ps, free, skill, pkill, pgrep,
snice, tload, top, uptime, vmstat, w, watch and pwdx. The ps command
displays a snapshot of running processes. The top command provides
a repetitive update of the statuses of running processes. The free
command displays the amounts of free and used memory on your
system. The skill command sends a terminate command (or another
specified signal) to a specified set of processes. The snice
command is used to change the scheduling priority of specified
processes. The tload command prints a graph of the current system
load average to a specified tty. The uptime command displays the
current time, how long the system has been running, how many users
are logged on, and system load averages for the past one, five,
and fifteen minutes. The w command displays a list of the users
who are currently logged on and what they are running. The watch
program watches a running program. The vmstat command displays
virtual memory statistics about processes, memory, paging, block
I/O, traps, and CPU activity. The pwdx command reports the current
working directory of a process or processes.
SourceForge のサイトを訪れると、gitリポジトリは https://gitlab.com/procps-ng/procps に移動したとあるので、GitLabのリポジトリから バージョン 3.3.10 のソース を取得する。
topコマンドのソースファイルは https://gitlab.com/procps-ng/procps/-/blob/v3.3.10/top/top.c で、
main関数は下の内容。 Loops変数 は -n オプションの値が設定され、指定された回数ループしたら終了する模様。初期値は -1 。
int main (int dont_care_argc, char **argv) {
(void)dont_care_argc;
before(*argv);
// +-------------+
wins_stage_1(); // top (sic) slice
configs_reads(); // > spread etc, <
parse_args(&argv[1]); // > lean stuff, <
whack_terminal(); // > onions etc. <
wins_stage_2(); // as bottom slice
// +-------------+
for (;;) {
struct timespec ts;
frame_make();
if (0 < Loops) --Loops;
if (!Loops) bye_bye(NULL);
ts.tv_sec = Rc.delay_time;
ts.tv_nsec = (Rc.delay_time - (int)Rc.delay_time) * 1000000000;
if (Batch)
pselect(0, NULL, NULL, NULL, &ts, NULL);
else {
if (ioa(&ts))
do_key(iokey(1));
}
/* note: that above ioa routine exists to consolidate all logic
which is susceptible to signal interrupt and must then
produce a screen refresh. in this main loop frame_make
assumes responsibility for such refreshes. other logic
in contact with users must deal more obliquely with an
interrupt/refresh (hint: Frames_signal + return code)!
(everything is perfectly justified plus right margins)
(are completely filled, but of course it must be luck)
*/
}
return 0;
} // end: main
プロセスの情報を読み取り出力を行うのは frame_make 関数なので、この関数を見てみると、procs_refresh() を呼び出している。いかにも、プロセスの情報を読み取っていそう。
procs_refresh 関数は libprocps のお作法に則って、 /proc/[pid]/stat からプロセスの情報を読んでいる。 https://qiita.com/rarul/items/e02b4f8faaf69890bc18 が参考になった。
主な変数
-
proc_t構造体 は プロセスの情報を管理し、 proc(5) に記載されている/proc/[pid]/statを読み取った情報 -
private_ppt変数 は 複数のproc_tを格納する領域 -
PROCTAB構造体 はopenprocの戻り値。readprocで使う。 -
read_something変数 は topコマンドの -H オプションで切り替わる。 -H オプションが指定されていればスレッドの情報も読み込むreadeither関数を指し、-H オプションが指定されていなければreadproc関数を指す。
読み込んだ結果は グローバル変数 Winstk に保存されている。ところで GROUPSMAX のグループって何だろう。
/* /////////////////////////////////////////////////////////////// */
/* Special Section: multiple windows/field groups --------------- */
/* ( kind of a header within a header: constants, types & macros ) */
#define CAPTABMAX 9 /* max entries in each win's caps table */
#define GROUPSMAX 4 /* the max number of simultaneous windows */
#define WINNAMSIZ 4 /* size of RCW_t winname buf (incl '\0') */
#define GRPNAMSIZ WINNAMSIZ+2 /* window's name + number as in: '#:...' */
マルチウィンドウってあるけど、よくわからない。自分が知らないtopの機能かな。
procs_refresh 関数は大体わかったので、 frame_make関数に戻ろう。
procs_refresh 関数の次は summary_show 関数。その名前の通りサマリー情報、具体的にはターミナルの上部に表示される下の部分を表示している。
top - 00:08:11 up 0 min, 0 users, load average: 0.82, 0.18, 0.06
Tasks: 113 total, 3 running, 67 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2001104 total, 841628 free, 683812 used, 475664 buff/cache
KiB Swap: 499996 total, 499996 free, 0 used. 1170004 avail Mem
frame_make関数で 表示されるプロセス数 に関わっていそうなのは下の3つ。
-
Screen_rowsは ターミナルの縦方向の行数。 -
Msg_rowは表示済みの行数。summary_show関数で表示した行数をカウントしていた。これからは1件のプロセスを表示したら、カウントアップされるはず。 -
Max_linesは 表示できる上限なので、表示されるプロセス数の条件に相当する。
プロセスの表示を行っていそうなのは、この部分。
if (!Rc.mode_altscr) {
// only 1 window to show so, piece o' cake
w->winlines = w->rc.maxtasks ? w->rc.maxtasks : Max_lines;
scrlins = window_show(w, Max_lines);
} else {
// maybe NO window is visible but assume, pieces o' cakes
for (i = 0 ; i < GROUPSMAX; i++) {
if (CHKw(&Winstk[i], Show_TASKON)) {
frame_hlp(i, Max_lines - scrlins);
scrlins += window_show(&Winstk[i], Max_lines - scrlins);
}
if (Max_lines <= scrlins) break;
}
}
Rc.mode_altscr という変数 が出てくるので、定義を参照してみると
'A' - Alt display mode (multi task windows)
とあった。
topコマンドを実行中に (大文字の)A を入力すると4分割された状態に切り替わる。man(1)の和訳には「 別形式の表示モード」と書かれていた。 マルチウィンドウが分かってすっきり。
1:Def - 00:45:03 up 37 min, 0 users, load average: 0.02, 0.03, 0.00
Tasks: 106 total, 1 running, 64 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.5 us, 0.7 sy, 0.0 ni, 97.3 id, 0.0 wa, 0.0 hi, 0.0 si, 1.5 st
KiB Mem : 2001104 total, 1067372 free, 372336 used, 561396 buff/cache
KiB Swap: 499996 total, 499996 free, 0 used. 1445736 avail Mem
1 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4995 ec2-user 20 0 997004 140928 32220 S 0.0 7.0 0:03.44 node
3541 root 20 0 1370424 75024 49344 S 0.0 3.7 0:00.48 dockerd
4255 ec2-user 20 0 935204 64160 31148 S 0.0 3.2 0:04.28 node
3121 root 20 0 1369496 39924 28280 S 0.0 2.0 0:00.99 containerd
4961 ec2-user 20 0 684764 35556 28636 S 0.0 1.8 0:00.14 node
5342 ec2-user 20 0 582440 35132 25992 S 0.0 1.8 0:00.27 node
5328 ec2-user 20 0 583668 35040 25976 S 0.0 1.8 0:00.23 node
3855 root 20 0 723844 20528 13324 S 0.0 1.0 0:00.12 ssm-agent-worke
1150 root 20 0 55484 17780 17424 S 0.0 0.9 0:00.22 systemd-journal
3316 root 20 0 714416 13384 8572 S 0.0 0.7 0:00.08 amazon-ssm-agen
3306 root 20 0 257484 9024 7476 S 0.0 0.5 0:00.20 rsyslogd
4673 root 20 0 148504 8856 7540 S 0.0 0.4 0:00.02 sshd
4206 root 20 0 148496 8772 7460 S 0.0 0.4 0:00.01 sshd
3459 root 20 0 110828 7668 6632 S 0.0 0.4 0:00.01 sshd
2 PID PPID TIME+ %CPU %MEM PR NI S VIRT RES UID COMMAND
20055 4596 0:00.05 0.4 0.2 20 0 R 168956 4468 1000 top
1 0 0:01.59 0.2 0.3 20 0 S 41608 5320 0 systemd
4594 1 0:00.12 0.2 0.1 20 0 S 126132 2816 1000 tmux
2 0 0:00.00 0.0 0.0 20 0 S 0 0 0 kthreadd
4 2 0:00.00 0.0 0.0 0 -20 I 0 0 0 kworker/0:0H
6 2 0:00.01 0.0 0.0 0 -20 I 0 0 0 kworker/0:1H
7 2 0:00.00 0.0 0.0 0 -20 I 0 0 0 mm_percpu_wq
8 2 0:00.07 0.0 0.0 20 0 S 0 0 0 ksoftirqd/0
9 2 0:00.31 0.0 0.0 20 0 I 0 0 0 rcu_sched
10 2 0:00.00 0.0 0.0 20 0 I 0 0 0 rcu_bh
11 2 0:00.01 0.0 0.0 rt 0 S 0 0 0 migration/0
12 2 0:00.00 0.0 0.0 rt 0 S 0 0 0 watchdog/0
13 2 0:00.00 0.0 0.0 20 0 S 0 0 0 cpuhp/0
14 2 0:00.00 0.0 0.0 20 0 S 0 0 0 cpuhp/1
3 PID %MEM VIRT RES CODE DATA SHR nMaj nDRT %CPU COMMAND
4995 7.0 997004 140928 39632 203412 32220 22 0 0.0 node
3541 3.7 1370424 75024 55612 179852 49344 374 0 0.0 dockerd
4255 3.2 935204 64160 39632 136088 31148 305 0 0.0 node
3121 2.0 1369496 39924 33360 132200 28280 274 0 0.0 containerd
4961 1.8 684764 35556 39632 101144 28636 0 0 0.0 node
5342 1.8 582440 35132 39632 62180 25992 0 0 0.0 node
5328 1.8 583668 35040 39632 62384 25976 0 0 0.0 node
3855 1.0 723844 20528 8516 47320 13324 129 0 0.0 ssm-agent-worke
1150 0.9 55484 17780 304 580 17424 127 0 0.0 systemd-journal
3316 0.7 714416 13384 4960 46736 8572 67 0 0.0 amazon-ssm-agen
3306 0.5 257484 9024 584 22056 7476 22 0 0.0 rsyslogd
4673 0.4 148504 8856 780 1288 7540 0 0 0.0 sshd
4206 0.4 148496 8772 780 1280 7460 0 0 0.0 sshd
3459 0.4 110828 7668 780 1040 6632 8 0 0.0 sshd
4 PID PPID UID USER RUSER TTY TIME+ %CPU %MEM S COMMAND
2385 1 32 rpc rpc ? 0:00.01 0.0 0.2 S rpcbind
1 0 0 root root ? 0:01.59 0.2 0.3 S systemd
2 0 0 root root ? 0:00.00 0.0 0.0 S kthreadd
4 2 0 root root ? 0:00.00 0.0 0.0 I kworker/0:0H
6 2 0 root root ? 0:00.01 0.0 0.0 I kworker/0:1H
7 2 0 root root ? 0:00.00 0.0 0.0 I mm_percpu_wq
8 2 0 root root ? 0:00.07 0.0 0.0 S ksoftirqd/0
9 2 0 root root ? 0:00.31 0.0 0.0 I rcu_sched
10 2 0 root root ? 0:00.00 0.0 0.0 I rcu_bh
11 2 0 root root ? 0:00.01 0.0 0.0 S migration/0
12 2 0 root root ? 0:00.00 0.0 0.0 S watchdog/0
13 2 0 root root ? 0:00.00 0.0 0.0 S cpuhp/0
14 2 0 root root ? 0:00.00 0.0 0.0 S cpuhp/1
15 2 0 root root ? 0:00.00 0.0 0.0 S watchdog/1
16 2 0 root root ? 0:00.11 0.0 0.0 S migration/1
シングルウィンドウで表示を行っているのは下の部分。
scrlins = window_show(w, Max_lines);
window_show 関数 に渡している w変数 は
WIN_t *w = Curwin; // avoid gcc bloat with a local copy
って書かれている。procs_refresh 関数で取得できたプロセスの情報は グローバルな Winstk 変数に渡している。違いは何?と思い、宣言部分を見てみる。
/* Our four WIN_t's, and which of those is considered the 'current'
window (ie. which window is associated with any summ info displayed
and to which window commands are directed) */
static WIN_t Winstk [GROUPSMAX];
static WIN_t *Curwin;
雰囲気だけど、 Curwin は Winstk の先頭?表示したいグループ?を指していそう
次は プロセスの表示を行っている window_show 関数を見ていこう
if (CHKw(q, Show_FOREST)) の Show_FOREST が気になったので確認すると、 プロセスをツリー表示する V キーのフラグだった。何かの時に使えそう。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 41608 5344 3832 S 0.0 0.3 0:01.77 systemd
1150 root 20 0 55484 18760 18404 S 0.0 0.9 0:00.30 `- systemd-journal
1390 root 20 0 116744 2148 1888 S 0.0 0.1 0:00.00 `- lvmetad
1803 root 20 0 46016 3832 3004 S 0.0 0.2 0:00.17 `- systemd-udevd
2289 root 16 -4 59736 2060 1652 S 0.0 0.1 0:00.03 `- auditd
2367 dbus 20 0 58272 4096 3636 S 0.0 0.2 0:00.41 `- dbus-daemon
2385 rpc 20 0 67268 3292 2756 S 0.0 0.2 0:00.01 `- rpcbind
2417 libstor+ 20 0 12620 1848 1680 S 0.0 0.1 0:00.01 `- lsmd
2419 root 20 0 28468 2996 2652 S 0.0 0.1 0:00.21 `- systemd-logind
2434 root 20 0 101848 1664 1496 S 0.0 0.1 0:00.08 `- irqbalance
2575 rngd 20 0 94076 4592 3740 S 0.0 0.2 0:00.00 `- rngd
2636 chrony 20 0 120316 3636 3136 S 0.0 0.2 0:00.06 `- chronyd
2649 root 20 0 211992 3260 2784 S 0.0 0.2 0:00.00 `- gssproxy
2989 root 20 0 98660 4508 2456 S 0.0 0.2 0:00.00 `- dhclient
3034 root 20 0 98660 4008 1988 S 0.0 0.2 0:00.00 `- dhclient
3121 root 20 0 1369496 39924 28280 S 0.0 2.0 0:01.83 `- containerd
3244 root 20 0 81752 4340 3480 S 0.0 0.2 0:00.01 `- master
3252 postfix 20 0 81856 5720 4860 S 0.0 0.3 0:00.01 `- pickup
3253 postfix 20 0 82036 5780 4888 S 0.0 0.3 0:00.00 `- qmgr
3306 root 20 0 257484 9024 7476 S 0.0 0.5 0:00.32 `- rsyslogd
3316 root 20 0 714416 13256 8572 S 0.0 0.7 0:00.16 `- amazon-ssm-agen
3855 root 20 0 723844 20588 13388 S 0.0 1.0 0:00.17 `- ssm-agent-worke
3459 root 20 0 110828 7668 6632 S 0.0 0.4 0:00.01 `- sshd
4206 root 20 0 148496 8772 7460 S 0.0 0.4 0:00.01 `- sshd
4253 ec2-user 20 0 149412 5264 3044 S 0.0 0.3 0:00.44 `- sshd
4254 ec2-user 20 0 9424 2324 2120 S 0.0 0.1 0:00.00 `- bash
4255 ec2-user 20 0 937252 66520 31148 S 0.0 3.3 0:07.44 `- node
4586 ec2-user 20 0 119488 2520 2324 S 0.0 0.1 0:00.00 `- tmux
4995 ec2-user 20 0 997004 141036 32220 S 0.0 7.0 0:03.65 `- node
5328 ec2-user 20 0 583668 38568 28392 S 0.0 1.9 0:00.31 `- node
5342 ec2-user 20 0 582440 38396 28408 S 0.0 1.9 0:00.35 `- node
task_show 関数は %CPU や %MEM などの表示する列を単位に switch case が書かれていた。
%CPU の部分 はこうなっていた。
case EU_CPU:
{ float u = (float)p->pcpu;
#ifndef TREE_VCPUOFF
#ifndef TREE_VWINALL
if (q == Curwin) // note: the following is NOT indented
#endif
if (CHKw(q, Show_FOREST)) u += Hide_cpu[idx];
u *= Frame_etscale;
if (p->pad_2 != 'x' && u > 100.0 * p->nlwp) u = 100.0 * p->nlwp;
#else
u *= Frame_etscale;
/* process can't use more %cpu than number of threads it has
( thanks Jaromir Capik <jcapik@redhat.com> ) */
if (u > 100.0 * p->nlwp) u = 100.0 * p->nlwp;
#endif
if (u > Cpu_pmax) u = Cpu_pmax;
cp = scale_pcnt(u, W, Jn);
}
break;
CPU使用率の元になる値は proc_t の pcpu だった。 /proc/[pid]/state で得られるのはCPUが割り当てられた時間なので、割合ではない。スルーした部分で pcpu を求めていそう。
(14) utime %lu
このプロセスがユーザーモードでスケジューリングされた時間の合計。 clock tick 単位で計測される (sysconf(_SC_CLK_TCK) で割った値が表示される)。 この値にはゲスト時間 guest_time (仮想 CPU の実行に消費された時間) も含まれる。これは、ゲスト時間のフィールドを認識しないアプリケーションにおいて、ゲスト時間分を計算に入れ損ねないようにするためである。
(15) stime %lu
プロセスのカーネルモードでの実行時間 (単位 jiffies)。 このプロセスがカーネルモードでスケジューリングされた時間の合計。 clock tick 単位で計測される (sysconf(_SC_CLK_TCK) で割った値が表示される)。
http://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html より。
proc_t の pcpu を求めている場所のあたりをつけるとしたら、プロセスの情報を取得するprocs_refresh 関数で readproc 関数を実装した後に実行している procs_hlp 関数。 コメントにそう書いてある。それに加えて、プロセス情報を読み込むループの前にも実行している。
procs_hlp(NULL); // prep for a new frame
if (NULL == (PT = openproc(Frames_libflags, Monpids)))
error_exit(fmtmk(N_fmt(FAIL_openlib_fmt), strerror(errno)));
read_something = Thread_mode ? readeither : readproc;
for (;;) {
if (n_used == n_alloc) {
/* we're subject to integer overflow if total linux tasks ever approach |
400+ million (but, do you think memory might be the bigger problem?) | */
n_alloc = 10 + ((n_alloc * 5) / 4); // grow by over 25%
private_ppt = alloc_r(private_ppt, sizeof(proc_t *) * n_alloc);
// ensure NULL pointers for the additional memory just acquired
memset(private_ppt + n_used, 0, sizeof(proc_t *) * (n_alloc - n_used));
}
// on the way to n_alloc, the library will allocate the underlying
// proc_t storage whenever our private_ppt[] pointer is NULL...
if (!(ptask = read_something(PT, private_ppt[n_used]))) break;
procs_hlp((private_ppt[n_used] = ptask)); // tally this proc_t
}
なので、 procs_hlp を見ていく。
static void procs_hlp (proc_t *this) {
#ifdef OFF_HST_HASH
static unsigned maxt_sav = 0; // prior frame's max tasks
#endif
TIC_t tics;
HST_t *h;
if (!this) {
static double uptime_sav;
double uptime_cur;
float et;
void *v;
uptime(&uptime_cur, NULL);
et = uptime_cur - uptime_sav;
if (et < 0.01) et = 0.005;
uptime_sav = uptime_cur;
// if in Solaris mode, adjust our scaling for all cpus
Frame_etscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : smp_num_cpus));
#ifdef OFF_HST_HASH
maxt_sav = Frame_maxtask;
#endif
Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;
// prep for saving this frame's HST_t's (and reuse mem each time around)
v = PHist_sav;
PHist_sav = PHist_new;
PHist_new = v;
#ifdef OFF_HST_HASH
// prep for binary search by sorting the last frame's HST_t's
qsort(PHist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
#else
v = PHash_sav;
PHash_sav = PHash_new;
PHash_new = v;
memcpy(PHash_new, HHash_nul, sizeof(HHash_nul));
#endif
return;
}
switch (this->state) {
case 'R':
Frame_running++;
break;
case 't': // 't' (tracing stop)
case 'T':
Frame_stopped++;
break;
case 'Z':
Frame_zombied++;
break;
default:
/* currently: 'D' (disk sleep),
'I' (idle),
'P' (parked),
'S' (sleeping),
'X' (dead - actually 'dying' & probably never seen)
*/
Frame_sleepin++;
break;
}
if (Frame_maxtask+1 >= HHist_siz) {
/* we're subject to integer overflow if total linux tasks ever approach |
400+ million (but, do you think memory might be the bigger problem?) | */
HHist_siz = HHist_siz * 5 / 4 + 100;
PHist_sav = alloc_r(PHist_sav, sizeof(HST_t) * HHist_siz);
PHist_new = alloc_r(PHist_new, sizeof(HST_t) * HHist_siz);
}
/* calculate time in this process; the sum of user time (utime) and
system time (stime) -- but PLEASE dont waste time and effort on
calcs and saves that go unused, like the old top! */
PHist_new[Frame_maxtask].pid = this->tid;
PHist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
// finally, save major/minor fault counts in case the deltas are displayable
PHist_new[Frame_maxtask].maj = this->maj_flt;
PHist_new[Frame_maxtask].min = this->min_flt;
#ifdef OFF_HST_HASH
// find matching entry from previous frame and make stuff elapsed
if ((h = hstbsrch(PHist_sav, maxt_sav - 1, this->tid))) {
tics -= h->tics;
this->maj_delta = this->maj_flt - h->maj;
this->min_delta = this->min_flt - h->min;
}
#else
// hash & save for the next frame
hstput(Frame_maxtask);
// find matching entry from previous frame and make stuff elapsed
if ((h = hstget(this->tid))) {
tics -= h->tics;
this->maj_delta = this->maj_flt - h->maj;
this->min_delta = this->min_flt - h->min;
}
#endif
/* we're just saving elapsed tics, to be converted into %cpu if
this task wins it's displayable screen row lottery... */
this->pcpu = tics;
// shout this to the world with the final call (or us the next time in)
Frame_maxtask++;
} // end: procs_hlp
procs_hlp 関数は 大きく2つに分かれていて
前処理として、 procs_hlp(NULL); で呼び出された場合と、 集計処理として procs_hlp((private_ppt[n_used] = ptask)); で呼び出される場合がある。
前処理は if (!this) { の部分。集計処理は switch (this->state) { 以降の部分。
前処理のうち、CPU使用率に関係がありそうなのは、下の2つ。
-
Frame_etscaleは topコマンドの計測間隔は -d オプション でされる。前回の計測から今回の計測までの経過時間(et変数に入っている) を調べ、その経過時間内に発生しうる tick 数。 -
PHist_savとPHist_newの入れ替え、および、PHash_savとPHash_newの入れ替え。
Frame_etscale は使われる時に書く予定。
PHist_xxx は プロセスの履歴を記録している HST_t 構造体の動的な配列。
typedef struct HST_t {
TIC_t tics; // last frame's tics count
unsigned long maj, min; // last frame's maj/min_flt counts
int pid; // record 'key'
int lnk; // next on hash chain
} HST_t;
CPU利用率でいえば必要なのは ティック数を管理している tics。 majとminはページフォルトの メジャーとマイナーだった。これらを pid に紐づけて管理している。
_sav と _new の2つあるのは、履歴といっても前回の値だけで足りるので、前回分と今回分の2つだけがある。メモリ管理としては、前回と今回が交互に入れ替わっている。
もう一つの、PHash_xxx は 前回の値を知るための情報。ハッシュマップになっていて キーは pid、値は PHash_sav の n番目。